Merge branch 'master' into enable_udp_testing
diff --git a/.gitignore b/.gitignore
index 09223fa..ed015b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 # C/C++ build outputs
+.build/
 bins
 gens
 libs
@@ -99,3 +100,6 @@
 
 # Git generated files for conflicting
 *.orig
+
+# IDE specific folder for JetBrains IDEs
+.idea/
diff --git a/.gitmodules b/.gitmodules
index ce647f3..3665cde 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,7 +4,7 @@
 [submodule "third_party/protobuf"]
 	path = third_party/protobuf
 	url = https://github.com/google/protobuf.git
-	branch = 3.0.0-beta-3
+	branch = 3.0.x
 [submodule "third_party/gflags"]
 	path = third_party/gflags
 	url = https://github.com/gflags/gflags.git
@@ -14,6 +14,6 @@
 [submodule "third_party/boringssl"]
 	path = third_party/boringssl
 	url = https://github.com/google/boringssl.git
-[submodule "third_party/nanopb"]
-	path = third_party/nanopb
-	url = https://github.com/nanopb/nanopb.git
+[submodule "third_party/thrift"]
+	path = third_party/thrift
+	url = https://github.com/apache/thrift.git
diff --git a/BUILD b/BUILD
index 8a6d7fd..bad6f3f 100644
--- a/BUILD
+++ b/BUILD
@@ -36,6 +36,8 @@
 
 licenses(["notice"])  # 3-clause BSD
 
+exports_files(["LICENSE"])
+
 package(default_visibility = ["//visibility:public"])
 
 
@@ -49,7 +51,9 @@
     "src/core/lib/support/backoff.h",
     "src/core/lib/support/block_annotate.h",
     "src/core/lib/support/env.h",
+    "src/core/lib/support/mpscq.h",
     "src/core/lib/support/murmur_hash.h",
+    "src/core/lib/support/percent_encoding.h",
     "src/core/lib/support/stack_lockfree.h",
     "src/core/lib/support/string.h",
     "src/core/lib/support/string_windows.h",
@@ -76,7 +80,9 @@
     "src/core/lib/support/log_linux.c",
     "src/core/lib/support/log_posix.c",
     "src/core/lib/support/log_windows.c",
+    "src/core/lib/support/mpscq.c",
     "src/core/lib/support/murmur_hash.c",
+    "src/core/lib/support/percent_encoding.c",
     "src/core/lib/support/slice.c",
     "src/core/lib/support/slice_buffer.c",
     "src/core/lib/support/stack_lockfree.c",
@@ -131,20 +137,17 @@
     "include/grpc/support/tls_msvc.h",
     "include/grpc/support/tls_pthread.h",
     "include/grpc/support/useful.h",
-    "include/grpc/impl/codegen/alloc.h",
     "include/grpc/impl/codegen/atm.h",
     "include/grpc/impl/codegen/atm_gcc_atomic.h",
     "include/grpc/impl/codegen/atm_gcc_sync.h",
     "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/log.h",
+    "include/grpc/impl/codegen/gpr_types.h",
     "include/grpc/impl/codegen/port_platform.h",
     "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/slice_buffer.h",
     "include/grpc/impl/codegen/sync.h",
     "include/grpc/impl/codegen/sync_generic.h",
     "include/grpc/impl/codegen/sync_posix.h",
     "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/impl/codegen/time.h",
   ],
   includes = [
     "include",
@@ -165,9 +168,11 @@
     "src/core/lib/channel/compress_filter.h",
     "src/core/lib/channel/connected_channel.h",
     "src/core/lib/channel/context.h",
+    "src/core/lib/channel/deadline_filter.h",
     "src/core/lib/channel/handshaker.h",
     "src/core/lib/channel/http_client_filter.h",
     "src/core/lib/channel/http_server_filter.h",
+    "src/core/lib/channel/message_size_filter.h",
     "src/core/lib/compression/algorithm_metadata.h",
     "src/core/lib/compression/message_compress.h",
     "src/core/lib/debug/trace.h",
@@ -175,6 +180,7 @@
     "src/core/lib/http/httpcli.h",
     "src/core/lib/http/parser.h",
     "src/core/lib/iomgr/closure.h",
+    "src/core/lib/iomgr/combiner.h",
     "src/core/lib/iomgr/endpoint.h",
     "src/core/lib/iomgr/endpoint_pair.h",
     "src/core/lib/iomgr/error.h",
@@ -236,6 +242,7 @@
     "src/core/lib/transport/metadata.h",
     "src/core/lib/transport/metadata_batch.h",
     "src/core/lib/transport/static_metadata.h",
+    "src/core/lib/transport/timeout_encoding.h",
     "src/core/lib/transport/transport.h",
     "src/core/lib/transport/transport_impl.h",
     "src/core/ext/transport/chttp2/transport/bin_decoder.h",
@@ -257,7 +264,6 @@
     "src/core/ext/transport/chttp2/transport/internal.h",
     "src/core/ext/transport/chttp2/transport/status_conversion.h",
     "src/core/ext/transport/chttp2/transport/stream_map.h",
-    "src/core/ext/transport/chttp2/transport/timeout_encoding.h",
     "src/core/ext/transport/chttp2/transport/varint.h",
     "src/core/ext/transport/chttp2/alpn/alpn.h",
     "src/core/lib/security/context/security_context.h",
@@ -286,8 +292,8 @@
     "src/core/lib/tsi/transport_security_interface.h",
     "src/core/ext/client_config/client_channel.h",
     "src/core/ext/client_config/client_channel_factory.h",
-    "src/core/ext/client_config/client_config.h",
     "src/core/ext/client_config/connector.h",
+    "src/core/ext/client_config/http_connect_handshaker.h",
     "src/core/ext/client_config/initial_connect_string.h",
     "src/core/ext/client_config/lb_policy.h",
     "src/core/ext/client_config/lb_policy_factory.h",
@@ -296,30 +302,37 @@
     "src/core/ext/client_config/resolver.h",
     "src/core/ext/client_config/resolver_factory.h",
     "src/core/ext/client_config/resolver_registry.h",
+    "src/core/ext/client_config/resolver_result.h",
     "src/core/ext/client_config/subchannel.h",
-    "src/core/ext/client_config/subchannel_call_holder.h",
     "src/core/ext/client_config/subchannel_index.h",
     "src/core/ext/client_config/uri_parser.h",
+    "src/core/ext/lb_policy/grpclb/grpclb.h",
     "src/core/ext/lb_policy/grpclb/load_balancer_api.h",
     "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h",
     "src/core/ext/load_reporting/load_reporting.h",
     "src/core/ext/load_reporting/load_reporting_filter.h",
     "src/core/ext/census/aggregation.h",
+    "src/core/ext/census/base_resources.h",
     "src/core/ext/census/census_interface.h",
     "src/core/ext/census/census_rpc_stats.h",
     "src/core/ext/census/gen/census.pb.h",
+    "src/core/ext/census/gen/trace_context.pb.h",
     "src/core/ext/census/grpc_filter.h",
     "src/core/ext/census/mlog.h",
+    "src/core/ext/census/resource.h",
     "src/core/ext/census/rpc_metric_id.h",
+    "src/core/ext/census/trace_context.h",
     "src/core/lib/surface/init.c",
     "src/core/lib/channel/channel_args.c",
     "src/core/lib/channel/channel_stack.c",
     "src/core/lib/channel/channel_stack_builder.c",
     "src/core/lib/channel/compress_filter.c",
     "src/core/lib/channel/connected_channel.c",
+    "src/core/lib/channel/deadline_filter.c",
     "src/core/lib/channel/handshaker.c",
     "src/core/lib/channel/http_client_filter.c",
     "src/core/lib/channel/http_server_filter.c",
+    "src/core/lib/channel/message_size_filter.c",
     "src/core/lib/compression/compression.c",
     "src/core/lib/compression/message_compress.c",
     "src/core/lib/debug/trace.c",
@@ -327,6 +340,7 @@
     "src/core/lib/http/httpcli.c",
     "src/core/lib/http/parser.c",
     "src/core/lib/iomgr/closure.c",
+    "src/core/lib/iomgr/combiner.c",
     "src/core/lib/iomgr/endpoint.c",
     "src/core/lib/iomgr/endpoint_pair_posix.c",
     "src/core/lib/iomgr/endpoint_pair_windows.c",
@@ -398,6 +412,7 @@
     "src/core/lib/transport/metadata.c",
     "src/core/lib/transport/metadata_batch.c",
     "src/core/lib/transport/static_metadata.c",
+    "src/core/lib/transport/timeout_encoding.c",
     "src/core/lib/transport/transport.c",
     "src/core/lib/transport/transport_op_string.c",
     "src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c",
@@ -420,7 +435,6 @@
     "src/core/ext/transport/chttp2/transport/status_conversion.c",
     "src/core/ext/transport/chttp2/transport/stream_lists.c",
     "src/core/ext/transport/chttp2/transport/stream_map.c",
-    "src/core/ext/transport/chttp2/transport/timeout_encoding.c",
     "src/core/ext/transport/chttp2/transport/varint.c",
     "src/core/ext/transport/chttp2/transport/writing.c",
     "src/core/ext/transport/chttp2/alpn/alpn.c",
@@ -456,10 +470,10 @@
     "src/core/ext/client_config/channel_connectivity.c",
     "src/core/ext/client_config/client_channel.c",
     "src/core/ext/client_config/client_channel_factory.c",
-    "src/core/ext/client_config/client_config.c",
     "src/core/ext/client_config/client_config_plugin.c",
     "src/core/ext/client_config/connector.c",
     "src/core/ext/client_config/default_initial_connect_string.c",
+    "src/core/ext/client_config/http_connect_handshaker.c",
     "src/core/ext/client_config/initial_connect_string.c",
     "src/core/ext/client_config/lb_policy.c",
     "src/core/ext/client_config/lb_policy_factory.c",
@@ -468,14 +482,15 @@
     "src/core/ext/client_config/resolver.c",
     "src/core/ext/client_config/resolver_factory.c",
     "src/core/ext/client_config/resolver_registry.c",
+    "src/core/ext/client_config/resolver_result.c",
     "src/core/ext/client_config/subchannel.c",
-    "src/core/ext/client_config/subchannel_call_holder.c",
     "src/core/ext/client_config/subchannel_index.c",
     "src/core/ext/client_config/uri_parser.c",
     "src/core/ext/transport/chttp2/server/insecure/server_chttp2.c",
     "src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c",
     "src/core/ext/transport/chttp2/client/insecure/channel_create.c",
     "src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c",
+    "src/core/ext/lb_policy/grpclb/grpclb.c",
     "src/core/ext/lb_policy/grpclb/load_balancer_api.c",
     "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
     "src/core/ext/lb_policy/pick_first/pick_first.c",
@@ -484,8 +499,10 @@
     "src/core/ext/resolver/sockaddr/sockaddr_resolver.c",
     "src/core/ext/load_reporting/load_reporting.c",
     "src/core/ext/load_reporting/load_reporting_filter.c",
+    "src/core/ext/census/base_resources.c",
     "src/core/ext/census/context.c",
     "src/core/ext/census/gen/census.pb.c",
+    "src/core/ext/census/gen/trace_context.pb.c",
     "src/core/ext/census/grpc_context.c",
     "src/core/ext/census/grpc_filter.c",
     "src/core/ext/census/grpc_plugin.c",
@@ -493,6 +510,8 @@
     "src/core/ext/census/mlog.c",
     "src/core/ext/census/operation.c",
     "src/core/ext/census/placeholders.c",
+    "src/core/ext/census/resource.c",
+    "src/core/ext/census/trace_context.c",
     "src/core/ext/census/tracing.c",
     "src/core/plugin_registry/grpc_plugin_registry.c",
   ],
@@ -502,30 +521,26 @@
     "include/grpc/compression.h",
     "include/grpc/grpc.h",
     "include/grpc/grpc_posix.h",
+    "include/grpc/grpc_security_constants.h",
     "include/grpc/status.h",
-    "include/grpc/impl/codegen/byte_buffer.h",
     "include/grpc/impl/codegen/byte_buffer_reader.h",
     "include/grpc/impl/codegen/compression_types.h",
     "include/grpc/impl/codegen/connectivity_state.h",
     "include/grpc/impl/codegen/grpc_types.h",
     "include/grpc/impl/codegen/propagation_bits.h",
     "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/alloc.h",
     "include/grpc/impl/codegen/atm.h",
     "include/grpc/impl/codegen/atm_gcc_atomic.h",
     "include/grpc/impl/codegen/atm_gcc_sync.h",
     "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/log.h",
+    "include/grpc/impl/codegen/gpr_types.h",
     "include/grpc/impl/codegen/port_platform.h",
     "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/slice_buffer.h",
     "include/grpc/impl/codegen/sync.h",
     "include/grpc/impl/codegen/sync_generic.h",
     "include/grpc/impl/codegen/sync_posix.h",
     "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/impl/codegen/time.h",
     "include/grpc/grpc_security.h",
-    "include/grpc/grpc_security_constants.h",
     "include/grpc/census.h",
   ],
   includes = [
@@ -554,9 +569,11 @@
     "src/core/lib/channel/compress_filter.h",
     "src/core/lib/channel/connected_channel.h",
     "src/core/lib/channel/context.h",
+    "src/core/lib/channel/deadline_filter.h",
     "src/core/lib/channel/handshaker.h",
     "src/core/lib/channel/http_client_filter.h",
     "src/core/lib/channel/http_server_filter.h",
+    "src/core/lib/channel/message_size_filter.h",
     "src/core/lib/compression/algorithm_metadata.h",
     "src/core/lib/compression/message_compress.h",
     "src/core/lib/debug/trace.h",
@@ -564,6 +581,7 @@
     "src/core/lib/http/httpcli.h",
     "src/core/lib/http/parser.h",
     "src/core/lib/iomgr/closure.h",
+    "src/core/lib/iomgr/combiner.h",
     "src/core/lib/iomgr/endpoint.h",
     "src/core/lib/iomgr/endpoint_pair.h",
     "src/core/lib/iomgr/error.h",
@@ -625,6 +643,7 @@
     "src/core/lib/transport/metadata.h",
     "src/core/lib/transport/metadata_batch.h",
     "src/core/lib/transport/static_metadata.h",
+    "src/core/lib/transport/timeout_encoding.h",
     "src/core/lib/transport/transport.h",
     "src/core/lib/transport/transport_impl.h",
     "third_party/objective_c/Cronet/cronet_c_for_grpc.h",
@@ -647,13 +666,12 @@
     "src/core/ext/transport/chttp2/transport/internal.h",
     "src/core/ext/transport/chttp2/transport/status_conversion.h",
     "src/core/ext/transport/chttp2/transport/stream_map.h",
-    "src/core/ext/transport/chttp2/transport/timeout_encoding.h",
     "src/core/ext/transport/chttp2/transport/varint.h",
     "src/core/ext/transport/chttp2/alpn/alpn.h",
     "src/core/ext/client_config/client_channel.h",
     "src/core/ext/client_config/client_channel_factory.h",
-    "src/core/ext/client_config/client_config.h",
     "src/core/ext/client_config/connector.h",
+    "src/core/ext/client_config/http_connect_handshaker.h",
     "src/core/ext/client_config/initial_connect_string.h",
     "src/core/ext/client_config/lb_policy.h",
     "src/core/ext/client_config/lb_policy_factory.h",
@@ -662,8 +680,8 @@
     "src/core/ext/client_config/resolver.h",
     "src/core/ext/client_config/resolver_factory.h",
     "src/core/ext/client_config/resolver_registry.h",
+    "src/core/ext/client_config/resolver_result.h",
     "src/core/ext/client_config/subchannel.h",
-    "src/core/ext/client_config/subchannel_call_holder.h",
     "src/core/ext/client_config/subchannel_index.h",
     "src/core/ext/client_config/uri_parser.h",
     "src/core/lib/security/context/security_context.h",
@@ -696,9 +714,11 @@
     "src/core/lib/channel/channel_stack_builder.c",
     "src/core/lib/channel/compress_filter.c",
     "src/core/lib/channel/connected_channel.c",
+    "src/core/lib/channel/deadline_filter.c",
     "src/core/lib/channel/handshaker.c",
     "src/core/lib/channel/http_client_filter.c",
     "src/core/lib/channel/http_server_filter.c",
+    "src/core/lib/channel/message_size_filter.c",
     "src/core/lib/compression/compression.c",
     "src/core/lib/compression/message_compress.c",
     "src/core/lib/debug/trace.c",
@@ -706,6 +726,7 @@
     "src/core/lib/http/httpcli.c",
     "src/core/lib/http/parser.c",
     "src/core/lib/iomgr/closure.c",
+    "src/core/lib/iomgr/combiner.c",
     "src/core/lib/iomgr/endpoint.c",
     "src/core/lib/iomgr/endpoint_pair_posix.c",
     "src/core/lib/iomgr/endpoint_pair_windows.c",
@@ -777,6 +798,7 @@
     "src/core/lib/transport/metadata.c",
     "src/core/lib/transport/metadata_batch.c",
     "src/core/lib/transport/static_metadata.c",
+    "src/core/lib/transport/timeout_encoding.c",
     "src/core/lib/transport/transport.c",
     "src/core/lib/transport/transport_op_string.c",
     "src/core/ext/transport/cronet/client/secure/cronet_channel_create.c",
@@ -802,17 +824,16 @@
     "src/core/ext/transport/chttp2/transport/status_conversion.c",
     "src/core/ext/transport/chttp2/transport/stream_lists.c",
     "src/core/ext/transport/chttp2/transport/stream_map.c",
-    "src/core/ext/transport/chttp2/transport/timeout_encoding.c",
     "src/core/ext/transport/chttp2/transport/varint.c",
     "src/core/ext/transport/chttp2/transport/writing.c",
     "src/core/ext/transport/chttp2/alpn/alpn.c",
     "src/core/ext/client_config/channel_connectivity.c",
     "src/core/ext/client_config/client_channel.c",
     "src/core/ext/client_config/client_channel_factory.c",
-    "src/core/ext/client_config/client_config.c",
     "src/core/ext/client_config/client_config_plugin.c",
     "src/core/ext/client_config/connector.c",
     "src/core/ext/client_config/default_initial_connect_string.c",
+    "src/core/ext/client_config/http_connect_handshaker.c",
     "src/core/ext/client_config/initial_connect_string.c",
     "src/core/ext/client_config/lb_policy.c",
     "src/core/ext/client_config/lb_policy_factory.c",
@@ -821,8 +842,8 @@
     "src/core/ext/client_config/resolver.c",
     "src/core/ext/client_config/resolver_factory.c",
     "src/core/ext/client_config/resolver_registry.c",
+    "src/core/ext/client_config/resolver_result.c",
     "src/core/ext/client_config/subchannel.c",
-    "src/core/ext/client_config/subchannel_call_holder.c",
     "src/core/ext/client_config/subchannel_index.c",
     "src/core/ext/client_config/uri_parser.c",
     "src/core/lib/http/httpcli_security_connector.c",
@@ -861,31 +882,27 @@
     "include/grpc/compression.h",
     "include/grpc/grpc.h",
     "include/grpc/grpc_posix.h",
+    "include/grpc/grpc_security_constants.h",
     "include/grpc/status.h",
-    "include/grpc/impl/codegen/byte_buffer.h",
     "include/grpc/impl/codegen/byte_buffer_reader.h",
     "include/grpc/impl/codegen/compression_types.h",
     "include/grpc/impl/codegen/connectivity_state.h",
     "include/grpc/impl/codegen/grpc_types.h",
     "include/grpc/impl/codegen/propagation_bits.h",
     "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/alloc.h",
     "include/grpc/impl/codegen/atm.h",
     "include/grpc/impl/codegen/atm_gcc_atomic.h",
     "include/grpc/impl/codegen/atm_gcc_sync.h",
     "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/log.h",
+    "include/grpc/impl/codegen/gpr_types.h",
     "include/grpc/impl/codegen/port_platform.h",
     "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/slice_buffer.h",
     "include/grpc/impl/codegen/sync.h",
     "include/grpc/impl/codegen/sync_generic.h",
     "include/grpc/impl/codegen/sync_posix.h",
     "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/impl/codegen/time.h",
     "include/grpc/grpc_cronet.h",
     "include/grpc/grpc_security.h",
-    "include/grpc/grpc_security_constants.h",
   ],
   includes = [
     "include",
@@ -908,9 +925,11 @@
     "src/core/lib/channel/compress_filter.h",
     "src/core/lib/channel/connected_channel.h",
     "src/core/lib/channel/context.h",
+    "src/core/lib/channel/deadline_filter.h",
     "src/core/lib/channel/handshaker.h",
     "src/core/lib/channel/http_client_filter.h",
     "src/core/lib/channel/http_server_filter.h",
+    "src/core/lib/channel/message_size_filter.h",
     "src/core/lib/compression/algorithm_metadata.h",
     "src/core/lib/compression/message_compress.h",
     "src/core/lib/debug/trace.h",
@@ -918,6 +937,7 @@
     "src/core/lib/http/httpcli.h",
     "src/core/lib/http/parser.h",
     "src/core/lib/iomgr/closure.h",
+    "src/core/lib/iomgr/combiner.h",
     "src/core/lib/iomgr/endpoint.h",
     "src/core/lib/iomgr/endpoint_pair.h",
     "src/core/lib/iomgr/error.h",
@@ -979,6 +999,7 @@
     "src/core/lib/transport/metadata.h",
     "src/core/lib/transport/metadata_batch.h",
     "src/core/lib/transport/static_metadata.h",
+    "src/core/lib/transport/timeout_encoding.h",
     "src/core/lib/transport/transport.h",
     "src/core/lib/transport/transport_impl.h",
     "src/core/ext/transport/chttp2/transport/bin_decoder.h",
@@ -1000,13 +1021,12 @@
     "src/core/ext/transport/chttp2/transport/internal.h",
     "src/core/ext/transport/chttp2/transport/status_conversion.h",
     "src/core/ext/transport/chttp2/transport/stream_map.h",
-    "src/core/ext/transport/chttp2/transport/timeout_encoding.h",
     "src/core/ext/transport/chttp2/transport/varint.h",
     "src/core/ext/transport/chttp2/alpn/alpn.h",
     "src/core/ext/client_config/client_channel.h",
     "src/core/ext/client_config/client_channel_factory.h",
-    "src/core/ext/client_config/client_config.h",
     "src/core/ext/client_config/connector.h",
+    "src/core/ext/client_config/http_connect_handshaker.h",
     "src/core/ext/client_config/initial_connect_string.h",
     "src/core/ext/client_config/lb_policy.h",
     "src/core/ext/client_config/lb_policy_factory.h",
@@ -1015,21 +1035,26 @@
     "src/core/ext/client_config/resolver.h",
     "src/core/ext/client_config/resolver_factory.h",
     "src/core/ext/client_config/resolver_registry.h",
+    "src/core/ext/client_config/resolver_result.h",
     "src/core/ext/client_config/subchannel.h",
-    "src/core/ext/client_config/subchannel_call_holder.h",
     "src/core/ext/client_config/subchannel_index.h",
     "src/core/ext/client_config/uri_parser.h",
     "src/core/ext/load_reporting/load_reporting.h",
     "src/core/ext/load_reporting/load_reporting_filter.h",
+    "src/core/ext/lb_policy/grpclb/grpclb.h",
     "src/core/ext/lb_policy/grpclb/load_balancer_api.h",
     "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h",
     "src/core/ext/census/aggregation.h",
+    "src/core/ext/census/base_resources.h",
     "src/core/ext/census/census_interface.h",
     "src/core/ext/census/census_rpc_stats.h",
     "src/core/ext/census/gen/census.pb.h",
+    "src/core/ext/census/gen/trace_context.pb.h",
     "src/core/ext/census/grpc_filter.h",
     "src/core/ext/census/mlog.h",
+    "src/core/ext/census/resource.h",
     "src/core/ext/census/rpc_metric_id.h",
+    "src/core/ext/census/trace_context.h",
     "src/core/lib/surface/init.c",
     "src/core/lib/surface/init_unsecure.c",
     "src/core/lib/channel/channel_args.c",
@@ -1037,9 +1062,11 @@
     "src/core/lib/channel/channel_stack_builder.c",
     "src/core/lib/channel/compress_filter.c",
     "src/core/lib/channel/connected_channel.c",
+    "src/core/lib/channel/deadline_filter.c",
     "src/core/lib/channel/handshaker.c",
     "src/core/lib/channel/http_client_filter.c",
     "src/core/lib/channel/http_server_filter.c",
+    "src/core/lib/channel/message_size_filter.c",
     "src/core/lib/compression/compression.c",
     "src/core/lib/compression/message_compress.c",
     "src/core/lib/debug/trace.c",
@@ -1047,6 +1074,7 @@
     "src/core/lib/http/httpcli.c",
     "src/core/lib/http/parser.c",
     "src/core/lib/iomgr/closure.c",
+    "src/core/lib/iomgr/combiner.c",
     "src/core/lib/iomgr/endpoint.c",
     "src/core/lib/iomgr/endpoint_pair_posix.c",
     "src/core/lib/iomgr/endpoint_pair_windows.c",
@@ -1118,6 +1146,7 @@
     "src/core/lib/transport/metadata.c",
     "src/core/lib/transport/metadata_batch.c",
     "src/core/lib/transport/static_metadata.c",
+    "src/core/lib/transport/timeout_encoding.c",
     "src/core/lib/transport/transport.c",
     "src/core/lib/transport/transport_op_string.c",
     "src/core/ext/transport/chttp2/server/insecure/server_chttp2.c",
@@ -1141,7 +1170,6 @@
     "src/core/ext/transport/chttp2/transport/status_conversion.c",
     "src/core/ext/transport/chttp2/transport/stream_lists.c",
     "src/core/ext/transport/chttp2/transport/stream_map.c",
-    "src/core/ext/transport/chttp2/transport/timeout_encoding.c",
     "src/core/ext/transport/chttp2/transport/varint.c",
     "src/core/ext/transport/chttp2/transport/writing.c",
     "src/core/ext/transport/chttp2/alpn/alpn.c",
@@ -1150,10 +1178,10 @@
     "src/core/ext/client_config/channel_connectivity.c",
     "src/core/ext/client_config/client_channel.c",
     "src/core/ext/client_config/client_channel_factory.c",
-    "src/core/ext/client_config/client_config.c",
     "src/core/ext/client_config/client_config_plugin.c",
     "src/core/ext/client_config/connector.c",
     "src/core/ext/client_config/default_initial_connect_string.c",
+    "src/core/ext/client_config/http_connect_handshaker.c",
     "src/core/ext/client_config/initial_connect_string.c",
     "src/core/ext/client_config/lb_policy.c",
     "src/core/ext/client_config/lb_policy_factory.c",
@@ -1162,20 +1190,23 @@
     "src/core/ext/client_config/resolver.c",
     "src/core/ext/client_config/resolver_factory.c",
     "src/core/ext/client_config/resolver_registry.c",
+    "src/core/ext/client_config/resolver_result.c",
     "src/core/ext/client_config/subchannel.c",
-    "src/core/ext/client_config/subchannel_call_holder.c",
     "src/core/ext/client_config/subchannel_index.c",
     "src/core/ext/client_config/uri_parser.c",
     "src/core/ext/resolver/dns/native/dns_resolver.c",
     "src/core/ext/resolver/sockaddr/sockaddr_resolver.c",
     "src/core/ext/load_reporting/load_reporting.c",
     "src/core/ext/load_reporting/load_reporting_filter.c",
+    "src/core/ext/lb_policy/grpclb/grpclb.c",
     "src/core/ext/lb_policy/grpclb/load_balancer_api.c",
     "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
     "src/core/ext/lb_policy/pick_first/pick_first.c",
     "src/core/ext/lb_policy/round_robin/round_robin.c",
+    "src/core/ext/census/base_resources.c",
     "src/core/ext/census/context.c",
     "src/core/ext/census/gen/census.pb.c",
+    "src/core/ext/census/gen/trace_context.pb.c",
     "src/core/ext/census/grpc_context.c",
     "src/core/ext/census/grpc_filter.c",
     "src/core/ext/census/grpc_plugin.c",
@@ -1183,6 +1214,8 @@
     "src/core/ext/census/mlog.c",
     "src/core/ext/census/operation.c",
     "src/core/ext/census/placeholders.c",
+    "src/core/ext/census/resource.c",
+    "src/core/ext/census/trace_context.c",
     "src/core/ext/census/tracing.c",
     "src/core/plugin_registry/grpc_unsecure_plugin_registry.c",
   ],
@@ -1192,28 +1225,25 @@
     "include/grpc/compression.h",
     "include/grpc/grpc.h",
     "include/grpc/grpc_posix.h",
+    "include/grpc/grpc_security_constants.h",
     "include/grpc/status.h",
-    "include/grpc/impl/codegen/byte_buffer.h",
     "include/grpc/impl/codegen/byte_buffer_reader.h",
     "include/grpc/impl/codegen/compression_types.h",
     "include/grpc/impl/codegen/connectivity_state.h",
     "include/grpc/impl/codegen/grpc_types.h",
     "include/grpc/impl/codegen/propagation_bits.h",
     "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/alloc.h",
     "include/grpc/impl/codegen/atm.h",
     "include/grpc/impl/codegen/atm_gcc_atomic.h",
     "include/grpc/impl/codegen/atm_gcc_sync.h",
     "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/log.h",
+    "include/grpc/impl/codegen/gpr_types.h",
     "include/grpc/impl/codegen/port_platform.h",
     "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/slice_buffer.h",
     "include/grpc/impl/codegen/sync.h",
     "include/grpc/impl/codegen/sync_generic.h",
     "include/grpc/impl/codegen/sync_posix.h",
     "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/impl/codegen/time.h",
     "include/grpc/census.h",
   ],
   includes = [
@@ -1239,40 +1269,42 @@
     "src/cpp/common/secure_auth_context.h",
     "src/cpp/server/secure_server_credentials.h",
     "src/cpp/client/create_channel_internal.h",
+    "src/cpp/common/channel_filter.h",
     "src/cpp/server/dynamic_thread_pool.h",
     "src/cpp/server/thread_pool_interface.h",
+    "src/cpp/client/insecure_credentials.cc",
     "src/cpp/client/secure_credentials.cc",
     "src/cpp/common/auth_property_iterator.cc",
     "src/cpp/common/secure_auth_context.cc",
     "src/cpp/common/secure_channel_arguments.cc",
     "src/cpp/common/secure_create_auth_context.cc",
+    "src/cpp/server/insecure_server_credentials.cc",
     "src/cpp/server/secure_server_credentials.cc",
-    "src/cpp/client/channel.cc",
+    "src/cpp/client/channel_cc.cc",
     "src/cpp/client/client_context.cc",
     "src/cpp/client/create_channel.cc",
     "src/cpp/client/create_channel_internal.cc",
     "src/cpp/client/create_channel_posix.cc",
-    "src/cpp/client/credentials.cc",
+    "src/cpp/client/credentials_cc.cc",
     "src/cpp/client/generic_stub.cc",
-    "src/cpp/client/insecure_credentials.cc",
     "src/cpp/common/channel_arguments.cc",
-    "src/cpp/common/completion_queue.cc",
+    "src/cpp/common/channel_filter.cc",
+    "src/cpp/common/completion_queue_cc.cc",
     "src/cpp/common/core_codegen.cc",
     "src/cpp/common/rpc_method.cc",
     "src/cpp/server/async_generic_service.cc",
     "src/cpp/server/create_default_thread_pool.cc",
     "src/cpp/server/dynamic_thread_pool.cc",
-    "src/cpp/server/insecure_server_credentials.cc",
-    "src/cpp/server/server.cc",
     "src/cpp/server/server_builder.cc",
+    "src/cpp/server/server_cc.cc",
     "src/cpp/server/server_context.cc",
     "src/cpp/server/server_credentials.cc",
     "src/cpp/server/server_posix.cc",
-    "src/cpp/util/byte_buffer.cc",
-    "src/cpp/util/slice.cc",
+    "src/cpp/util/byte_buffer_cc.cc",
+    "src/cpp/util/slice_cc.cc",
     "src/cpp/util/status.cc",
     "src/cpp/util/string_ref.cc",
-    "src/cpp/util/time.cc",
+    "src/cpp/util/time_cc.cc",
     "src/cpp/codegen/codegen_init.cc",
   ],
   hdrs = [
@@ -1346,6 +1378,7 @@
     "include/grpc++/impl/codegen/service_type.h",
     "include/grpc++/impl/codegen/status.h",
     "include/grpc++/impl/codegen/status_code_enum.h",
+    "include/grpc++/impl/codegen/status_helper.h",
     "include/grpc++/impl/codegen/string_ref.h",
     "include/grpc++/impl/codegen/stub_options.h",
     "include/grpc++/impl/codegen/sync.h",
@@ -1353,27 +1386,23 @@
     "include/grpc++/impl/codegen/sync_no_cxx11.h",
     "include/grpc++/impl/codegen/sync_stream.h",
     "include/grpc++/impl/codegen/time.h",
-    "include/grpc/impl/codegen/byte_buffer.h",
     "include/grpc/impl/codegen/byte_buffer_reader.h",
     "include/grpc/impl/codegen/compression_types.h",
     "include/grpc/impl/codegen/connectivity_state.h",
     "include/grpc/impl/codegen/grpc_types.h",
     "include/grpc/impl/codegen/propagation_bits.h",
     "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/alloc.h",
     "include/grpc/impl/codegen/atm.h",
     "include/grpc/impl/codegen/atm_gcc_atomic.h",
     "include/grpc/impl/codegen/atm_gcc_sync.h",
     "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/log.h",
+    "include/grpc/impl/codegen/gpr_types.h",
     "include/grpc/impl/codegen/port_platform.h",
     "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/slice_buffer.h",
     "include/grpc/impl/codegen/sync.h",
     "include/grpc/impl/codegen/sync_generic.h",
     "include/grpc/impl/codegen/sync_posix.h",
     "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/impl/codegen/time.h",
   ],
   includes = [
     "include",
@@ -1425,6 +1454,7 @@
     "include/grpc++/impl/codegen/service_type.h",
     "include/grpc++/impl/codegen/status.h",
     "include/grpc++/impl/codegen/status_code_enum.h",
+    "include/grpc++/impl/codegen/status_helper.h",
     "include/grpc++/impl/codegen/string_ref.h",
     "include/grpc++/impl/codegen/stub_options.h",
     "include/grpc++/impl/codegen/sync.h",
@@ -1432,27 +1462,23 @@
     "include/grpc++/impl/codegen/sync_no_cxx11.h",
     "include/grpc++/impl/codegen/sync_stream.h",
     "include/grpc++/impl/codegen/time.h",
-    "include/grpc/impl/codegen/byte_buffer.h",
     "include/grpc/impl/codegen/byte_buffer_reader.h",
     "include/grpc/impl/codegen/compression_types.h",
     "include/grpc/impl/codegen/connectivity_state.h",
     "include/grpc/impl/codegen/grpc_types.h",
     "include/grpc/impl/codegen/propagation_bits.h",
     "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/alloc.h",
     "include/grpc/impl/codegen/atm.h",
     "include/grpc/impl/codegen/atm_gcc_atomic.h",
     "include/grpc/impl/codegen/atm_gcc_sync.h",
     "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/log.h",
+    "include/grpc/impl/codegen/gpr_types.h",
     "include/grpc/impl/codegen/port_platform.h",
     "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/slice_buffer.h",
     "include/grpc/impl/codegen/sync.h",
     "include/grpc/impl/codegen/sync_generic.h",
     "include/grpc/impl/codegen/sync_posix.h",
     "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/impl/codegen/time.h",
     "include/grpc++/impl/codegen/config_protobuf.h",
   ],
   includes = [
@@ -1470,35 +1496,37 @@
   name = "grpc++_unsecure",
   srcs = [
     "src/cpp/client/create_channel_internal.h",
+    "src/cpp/common/channel_filter.h",
     "src/cpp/server/dynamic_thread_pool.h",
     "src/cpp/server/thread_pool_interface.h",
+    "src/cpp/client/insecure_credentials.cc",
     "src/cpp/common/insecure_create_auth_context.cc",
-    "src/cpp/client/channel.cc",
+    "src/cpp/server/insecure_server_credentials.cc",
+    "src/cpp/client/channel_cc.cc",
     "src/cpp/client/client_context.cc",
     "src/cpp/client/create_channel.cc",
     "src/cpp/client/create_channel_internal.cc",
     "src/cpp/client/create_channel_posix.cc",
-    "src/cpp/client/credentials.cc",
+    "src/cpp/client/credentials_cc.cc",
     "src/cpp/client/generic_stub.cc",
-    "src/cpp/client/insecure_credentials.cc",
     "src/cpp/common/channel_arguments.cc",
-    "src/cpp/common/completion_queue.cc",
+    "src/cpp/common/channel_filter.cc",
+    "src/cpp/common/completion_queue_cc.cc",
     "src/cpp/common/core_codegen.cc",
     "src/cpp/common/rpc_method.cc",
     "src/cpp/server/async_generic_service.cc",
     "src/cpp/server/create_default_thread_pool.cc",
     "src/cpp/server/dynamic_thread_pool.cc",
-    "src/cpp/server/insecure_server_credentials.cc",
-    "src/cpp/server/server.cc",
     "src/cpp/server/server_builder.cc",
+    "src/cpp/server/server_cc.cc",
     "src/cpp/server/server_context.cc",
     "src/cpp/server/server_credentials.cc",
     "src/cpp/server/server_posix.cc",
-    "src/cpp/util/byte_buffer.cc",
-    "src/cpp/util/slice.cc",
+    "src/cpp/util/byte_buffer_cc.cc",
+    "src/cpp/util/slice_cc.cc",
     "src/cpp/util/status.cc",
     "src/cpp/util/string_ref.cc",
-    "src/cpp/util/time.cc",
+    "src/cpp/util/time_cc.cc",
     "src/cpp/codegen/codegen_init.cc",
   ],
   hdrs = [
@@ -1572,6 +1600,7 @@
     "include/grpc++/impl/codegen/service_type.h",
     "include/grpc++/impl/codegen/status.h",
     "include/grpc++/impl/codegen/status_code_enum.h",
+    "include/grpc++/impl/codegen/status_helper.h",
     "include/grpc++/impl/codegen/string_ref.h",
     "include/grpc++/impl/codegen/stub_options.h",
     "include/grpc++/impl/codegen/sync.h",
@@ -1579,27 +1608,23 @@
     "include/grpc++/impl/codegen/sync_no_cxx11.h",
     "include/grpc++/impl/codegen/sync_stream.h",
     "include/grpc++/impl/codegen/time.h",
-    "include/grpc/impl/codegen/byte_buffer.h",
     "include/grpc/impl/codegen/byte_buffer_reader.h",
     "include/grpc/impl/codegen/compression_types.h",
     "include/grpc/impl/codegen/connectivity_state.h",
     "include/grpc/impl/codegen/grpc_types.h",
     "include/grpc/impl/codegen/propagation_bits.h",
     "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/alloc.h",
     "include/grpc/impl/codegen/atm.h",
     "include/grpc/impl/codegen/atm_gcc_atomic.h",
     "include/grpc/impl/codegen/atm_gcc_sync.h",
     "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/log.h",
+    "include/grpc/impl/codegen/gpr_types.h",
     "include/grpc/impl/codegen/port_platform.h",
     "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/slice_buffer.h",
     "include/grpc/impl/codegen/sync.h",
     "include/grpc/impl/codegen/sync_generic.h",
     "include/grpc/impl/codegen/sync_posix.h",
     "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/impl/codegen/time.h",
   ],
   includes = [
     "include",
@@ -1609,7 +1634,6 @@
     "//external:protobuf_clib",
     ":gpr",
     ":grpc_unsecure",
-    ":grpc",
   ],
 )
 
@@ -1628,6 +1652,8 @@
     "src/compiler/node_generator_helpers.h",
     "src/compiler/objective_c_generator.h",
     "src/compiler/objective_c_generator_helpers.h",
+    "src/compiler/php_generator.h",
+    "src/compiler/php_generator_helpers.h",
     "src/compiler/python_generator.h",
     "src/compiler/ruby_generator.h",
     "src/compiler/ruby_generator_helpers-inl.h",
@@ -1637,6 +1663,7 @@
     "src/compiler/csharp_generator.cc",
     "src/compiler/node_generator.cc",
     "src/compiler/objective_c_generator.cc",
+    "src/compiler/php_generator.cc",
     "src/compiler/python_generator.cc",
     "src/compiler/ruby_generator.cc",
   ],
@@ -1697,7 +1724,9 @@
     "src/core/lib/support/log_linux.c",
     "src/core/lib/support/log_posix.c",
     "src/core/lib/support/log_windows.c",
+    "src/core/lib/support/mpscq.c",
     "src/core/lib/support/murmur_hash.c",
+    "src/core/lib/support/percent_encoding.c",
     "src/core/lib/support/slice.c",
     "src/core/lib/support/slice_buffer.c",
     "src/core/lib/support/stack_lockfree.c",
@@ -1752,25 +1781,24 @@
     "include/grpc/support/tls_msvc.h",
     "include/grpc/support/tls_pthread.h",
     "include/grpc/support/useful.h",
-    "include/grpc/impl/codegen/alloc.h",
     "include/grpc/impl/codegen/atm.h",
     "include/grpc/impl/codegen/atm_gcc_atomic.h",
     "include/grpc/impl/codegen/atm_gcc_sync.h",
     "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/log.h",
+    "include/grpc/impl/codegen/gpr_types.h",
     "include/grpc/impl/codegen/port_platform.h",
     "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/slice_buffer.h",
     "include/grpc/impl/codegen/sync.h",
     "include/grpc/impl/codegen/sync_generic.h",
     "include/grpc/impl/codegen/sync_posix.h",
     "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/impl/codegen/time.h",
     "src/core/lib/profiling/timers.h",
     "src/core/lib/support/backoff.h",
     "src/core/lib/support/block_annotate.h",
     "src/core/lib/support/env.h",
+    "src/core/lib/support/mpscq.h",
     "src/core/lib/support/murmur_hash.h",
+    "src/core/lib/support/percent_encoding.h",
     "src/core/lib/support/stack_lockfree.h",
     "src/core/lib/support/string.h",
     "src/core/lib/support/string_windows.h",
@@ -1797,9 +1825,11 @@
     "src/core/lib/channel/channel_stack_builder.c",
     "src/core/lib/channel/compress_filter.c",
     "src/core/lib/channel/connected_channel.c",
+    "src/core/lib/channel/deadline_filter.c",
     "src/core/lib/channel/handshaker.c",
     "src/core/lib/channel/http_client_filter.c",
     "src/core/lib/channel/http_server_filter.c",
+    "src/core/lib/channel/message_size_filter.c",
     "src/core/lib/compression/compression.c",
     "src/core/lib/compression/message_compress.c",
     "src/core/lib/debug/trace.c",
@@ -1807,6 +1837,7 @@
     "src/core/lib/http/httpcli.c",
     "src/core/lib/http/parser.c",
     "src/core/lib/iomgr/closure.c",
+    "src/core/lib/iomgr/combiner.c",
     "src/core/lib/iomgr/endpoint.c",
     "src/core/lib/iomgr/endpoint_pair_posix.c",
     "src/core/lib/iomgr/endpoint_pair_windows.c",
@@ -1878,6 +1909,7 @@
     "src/core/lib/transport/metadata.c",
     "src/core/lib/transport/metadata_batch.c",
     "src/core/lib/transport/static_metadata.c",
+    "src/core/lib/transport/timeout_encoding.c",
     "src/core/lib/transport/transport.c",
     "src/core/lib/transport/transport_op_string.c",
     "src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c",
@@ -1900,7 +1932,6 @@
     "src/core/ext/transport/chttp2/transport/status_conversion.c",
     "src/core/ext/transport/chttp2/transport/stream_lists.c",
     "src/core/ext/transport/chttp2/transport/stream_map.c",
-    "src/core/ext/transport/chttp2/transport/timeout_encoding.c",
     "src/core/ext/transport/chttp2/transport/varint.c",
     "src/core/ext/transport/chttp2/transport/writing.c",
     "src/core/ext/transport/chttp2/alpn/alpn.c",
@@ -1936,10 +1967,10 @@
     "src/core/ext/client_config/channel_connectivity.c",
     "src/core/ext/client_config/client_channel.c",
     "src/core/ext/client_config/client_channel_factory.c",
-    "src/core/ext/client_config/client_config.c",
     "src/core/ext/client_config/client_config_plugin.c",
     "src/core/ext/client_config/connector.c",
     "src/core/ext/client_config/default_initial_connect_string.c",
+    "src/core/ext/client_config/http_connect_handshaker.c",
     "src/core/ext/client_config/initial_connect_string.c",
     "src/core/ext/client_config/lb_policy.c",
     "src/core/ext/client_config/lb_policy_factory.c",
@@ -1948,14 +1979,15 @@
     "src/core/ext/client_config/resolver.c",
     "src/core/ext/client_config/resolver_factory.c",
     "src/core/ext/client_config/resolver_registry.c",
+    "src/core/ext/client_config/resolver_result.c",
     "src/core/ext/client_config/subchannel.c",
-    "src/core/ext/client_config/subchannel_call_holder.c",
     "src/core/ext/client_config/subchannel_index.c",
     "src/core/ext/client_config/uri_parser.c",
     "src/core/ext/transport/chttp2/server/insecure/server_chttp2.c",
     "src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c",
     "src/core/ext/transport/chttp2/client/insecure/channel_create.c",
     "src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c",
+    "src/core/ext/lb_policy/grpclb/grpclb.c",
     "src/core/ext/lb_policy/grpclb/load_balancer_api.c",
     "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
     "src/core/ext/lb_policy/pick_first/pick_first.c",
@@ -1964,8 +1996,10 @@
     "src/core/ext/resolver/sockaddr/sockaddr_resolver.c",
     "src/core/ext/load_reporting/load_reporting.c",
     "src/core/ext/load_reporting/load_reporting_filter.c",
+    "src/core/ext/census/base_resources.c",
     "src/core/ext/census/context.c",
     "src/core/ext/census/gen/census.pb.c",
+    "src/core/ext/census/gen/trace_context.pb.c",
     "src/core/ext/census/grpc_context.c",
     "src/core/ext/census/grpc_filter.c",
     "src/core/ext/census/grpc_plugin.c",
@@ -1973,6 +2007,8 @@
     "src/core/ext/census/mlog.c",
     "src/core/ext/census/operation.c",
     "src/core/ext/census/placeholders.c",
+    "src/core/ext/census/resource.c",
+    "src/core/ext/census/trace_context.c",
     "src/core/ext/census/tracing.c",
     "src/core/plugin_registry/grpc_plugin_registry.c",
   ],
@@ -1982,30 +2018,26 @@
     "include/grpc/compression.h",
     "include/grpc/grpc.h",
     "include/grpc/grpc_posix.h",
+    "include/grpc/grpc_security_constants.h",
     "include/grpc/status.h",
-    "include/grpc/impl/codegen/byte_buffer.h",
     "include/grpc/impl/codegen/byte_buffer_reader.h",
     "include/grpc/impl/codegen/compression_types.h",
     "include/grpc/impl/codegen/connectivity_state.h",
     "include/grpc/impl/codegen/grpc_types.h",
     "include/grpc/impl/codegen/propagation_bits.h",
     "include/grpc/impl/codegen/status.h",
-    "include/grpc/impl/codegen/alloc.h",
     "include/grpc/impl/codegen/atm.h",
     "include/grpc/impl/codegen/atm_gcc_atomic.h",
     "include/grpc/impl/codegen/atm_gcc_sync.h",
     "include/grpc/impl/codegen/atm_windows.h",
-    "include/grpc/impl/codegen/log.h",
+    "include/grpc/impl/codegen/gpr_types.h",
     "include/grpc/impl/codegen/port_platform.h",
     "include/grpc/impl/codegen/slice.h",
-    "include/grpc/impl/codegen/slice_buffer.h",
     "include/grpc/impl/codegen/sync.h",
     "include/grpc/impl/codegen/sync_generic.h",
     "include/grpc/impl/codegen/sync_posix.h",
     "include/grpc/impl/codegen/sync_windows.h",
-    "include/grpc/impl/codegen/time.h",
     "include/grpc/grpc_security.h",
-    "include/grpc/grpc_security_constants.h",
     "include/grpc/census.h",
     "src/core/lib/channel/channel_args.h",
     "src/core/lib/channel/channel_stack.h",
@@ -2013,9 +2045,11 @@
     "src/core/lib/channel/compress_filter.h",
     "src/core/lib/channel/connected_channel.h",
     "src/core/lib/channel/context.h",
+    "src/core/lib/channel/deadline_filter.h",
     "src/core/lib/channel/handshaker.h",
     "src/core/lib/channel/http_client_filter.h",
     "src/core/lib/channel/http_server_filter.h",
+    "src/core/lib/channel/message_size_filter.h",
     "src/core/lib/compression/algorithm_metadata.h",
     "src/core/lib/compression/message_compress.h",
     "src/core/lib/debug/trace.h",
@@ -2023,6 +2057,7 @@
     "src/core/lib/http/httpcli.h",
     "src/core/lib/http/parser.h",
     "src/core/lib/iomgr/closure.h",
+    "src/core/lib/iomgr/combiner.h",
     "src/core/lib/iomgr/endpoint.h",
     "src/core/lib/iomgr/endpoint_pair.h",
     "src/core/lib/iomgr/error.h",
@@ -2084,6 +2119,7 @@
     "src/core/lib/transport/metadata.h",
     "src/core/lib/transport/metadata_batch.h",
     "src/core/lib/transport/static_metadata.h",
+    "src/core/lib/transport/timeout_encoding.h",
     "src/core/lib/transport/transport.h",
     "src/core/lib/transport/transport_impl.h",
     "src/core/ext/transport/chttp2/transport/bin_decoder.h",
@@ -2105,7 +2141,6 @@
     "src/core/ext/transport/chttp2/transport/internal.h",
     "src/core/ext/transport/chttp2/transport/status_conversion.h",
     "src/core/ext/transport/chttp2/transport/stream_map.h",
-    "src/core/ext/transport/chttp2/transport/timeout_encoding.h",
     "src/core/ext/transport/chttp2/transport/varint.h",
     "src/core/ext/transport/chttp2/alpn/alpn.h",
     "src/core/lib/security/context/security_context.h",
@@ -2134,8 +2169,8 @@
     "src/core/lib/tsi/transport_security_interface.h",
     "src/core/ext/client_config/client_channel.h",
     "src/core/ext/client_config/client_channel_factory.h",
-    "src/core/ext/client_config/client_config.h",
     "src/core/ext/client_config/connector.h",
+    "src/core/ext/client_config/http_connect_handshaker.h",
     "src/core/ext/client_config/initial_connect_string.h",
     "src/core/ext/client_config/lb_policy.h",
     "src/core/ext/client_config/lb_policy_factory.h",
@@ -2144,21 +2179,26 @@
     "src/core/ext/client_config/resolver.h",
     "src/core/ext/client_config/resolver_factory.h",
     "src/core/ext/client_config/resolver_registry.h",
+    "src/core/ext/client_config/resolver_result.h",
     "src/core/ext/client_config/subchannel.h",
-    "src/core/ext/client_config/subchannel_call_holder.h",
     "src/core/ext/client_config/subchannel_index.h",
     "src/core/ext/client_config/uri_parser.h",
+    "src/core/ext/lb_policy/grpclb/grpclb.h",
     "src/core/ext/lb_policy/grpclb/load_balancer_api.h",
     "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h",
     "src/core/ext/load_reporting/load_reporting.h",
     "src/core/ext/load_reporting/load_reporting_filter.h",
     "src/core/ext/census/aggregation.h",
+    "src/core/ext/census/base_resources.h",
     "src/core/ext/census/census_interface.h",
     "src/core/ext/census/census_rpc_stats.h",
     "src/core/ext/census/gen/census.pb.h",
+    "src/core/ext/census/gen/trace_context.pb.h",
     "src/core/ext/census/grpc_filter.h",
     "src/core/ext/census/mlog.h",
+    "src/core/ext/census/resource.h",
     "src/core/ext/census/rpc_metric_id.h",
+    "src/core/ext/census/trace_context.h",
   ],
   includes = [
     "include",
@@ -2223,6 +2263,18 @@
 
 
 cc_binary(
+  name = "grpc_php_plugin",
+  srcs = [
+    "src/compiler/php_plugin.cc",
+  ],
+  deps = [
+    "//external:protobuf_compiler",
+    ":grpc_plugin_support",
+  ],
+)
+
+
+cc_binary(
   name = "grpc_python_plugin",
   srcs = [
     "src/compiler/python_plugin.cc",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c55825b..c419152 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -48,6 +48,12 @@
 set(PACKAGE_BUGREPORT "https://github.com/grpc/grpc/issues/")
 project(${PACKAGE_NAME} C CXX)
 
+if (NOT MSVC)
+  set(gRPC_INSTALL ON CACHE BOOL "Generate installation target")
+else()
+  set(gRPC_INSTALL OFF CACHE BOOL "Generate installation target")
+endif()
+
 set(gRPC_ZLIB_PROVIDER "module" CACHE STRING "Provider of zlib library")
 set_property(CACHE gRPC_ZLIB_PROVIDER PROPERTY STRINGS "module" "package")
 
@@ -59,6 +65,10 @@
 
 set(gRPC_USE_PROTO_LITE OFF CACHE BOOL "Use the protobuf-lite library")
 
+if (MSVC)
+  add_definitions( -D_WIN32_WINNT=0x600 )
+endif()
+
 if (gRPC_USE_PROTO_LITE)
   set(_gRPC_PROTOBUF_LIBRARY_NAME "libprotobuf-lite")
   add_definitions("-DGRPC_USE_PROTO_LITE")
@@ -98,6 +108,7 @@
     set(PROTOBUF_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/protobuf)
   endif()
   if(EXISTS "${PROTOBUF_ROOT_DIR}/cmake/CMakeLists.txt")
+    set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Link static runtime libraries")
     add_subdirectory(${PROTOBUF_ROOT_DIR}/cmake third_party/protobuf)
     if(TARGET ${_gRPC_PROTOBUF_LIBRARY_NAME})
       set(_gRPC_PROTOBUF_LIBRARIES ${_gRPC_PROTOBUF_LIBRARY_NAME})
@@ -180,7 +191,9 @@
   src/core/lib/support/log_linux.c
   src/core/lib/support/log_posix.c
   src/core/lib/support/log_windows.c
+  src/core/lib/support/mpscq.c
   src/core/lib/support/murmur_hash.c
+  src/core/lib/support/percent_encoding.c
   src/core/lib/support/slice.c
   src/core/lib/support/slice_buffer.c
   src/core/lib/support/stack_lockfree.c
@@ -246,20 +259,17 @@
   include/grpc/support/tls_msvc.h
   include/grpc/support/tls_pthread.h
   include/grpc/support/useful.h
-  include/grpc/impl/codegen/alloc.h
   include/grpc/impl/codegen/atm.h
   include/grpc/impl/codegen/atm_gcc_atomic.h
   include/grpc/impl/codegen/atm_gcc_sync.h
   include/grpc/impl/codegen/atm_windows.h
-  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/gpr_types.h
   include/grpc/impl/codegen/port_platform.h
   include/grpc/impl/codegen/slice.h
-  include/grpc/impl/codegen/slice_buffer.h
   include/grpc/impl/codegen/sync.h
   include/grpc/impl/codegen/sync_generic.h
   include/grpc/impl/codegen/sync_posix.h
   include/grpc/impl/codegen/sync_windows.h
-  include/grpc/impl/codegen/time.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -269,11 +279,13 @@
 endforeach()
 
   
-install(TARGETS gpr EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS gpr EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
   
 add_library(grpc
@@ -283,9 +295,11 @@
   src/core/lib/channel/channel_stack_builder.c
   src/core/lib/channel/compress_filter.c
   src/core/lib/channel/connected_channel.c
+  src/core/lib/channel/deadline_filter.c
   src/core/lib/channel/handshaker.c
   src/core/lib/channel/http_client_filter.c
   src/core/lib/channel/http_server_filter.c
+  src/core/lib/channel/message_size_filter.c
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/debug/trace.c
@@ -293,6 +307,7 @@
   src/core/lib/http/httpcli.c
   src/core/lib/http/parser.c
   src/core/lib/iomgr/closure.c
+  src/core/lib/iomgr/combiner.c
   src/core/lib/iomgr/endpoint.c
   src/core/lib/iomgr/endpoint_pair_posix.c
   src/core/lib/iomgr/endpoint_pair_windows.c
@@ -364,6 +379,7 @@
   src/core/lib/transport/metadata.c
   src/core/lib/transport/metadata_batch.c
   src/core/lib/transport/static_metadata.c
+  src/core/lib/transport/timeout_encoding.c
   src/core/lib/transport/transport.c
   src/core/lib/transport/transport_op_string.c
   src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
@@ -386,7 +402,6 @@
   src/core/ext/transport/chttp2/transport/status_conversion.c
   src/core/ext/transport/chttp2/transport/stream_lists.c
   src/core/ext/transport/chttp2/transport/stream_map.c
-  src/core/ext/transport/chttp2/transport/timeout_encoding.c
   src/core/ext/transport/chttp2/transport/varint.c
   src/core/ext/transport/chttp2/transport/writing.c
   src/core/ext/transport/chttp2/alpn/alpn.c
@@ -422,10 +437,10 @@
   src/core/ext/client_config/channel_connectivity.c
   src/core/ext/client_config/client_channel.c
   src/core/ext/client_config/client_channel_factory.c
-  src/core/ext/client_config/client_config.c
   src/core/ext/client_config/client_config_plugin.c
   src/core/ext/client_config/connector.c
   src/core/ext/client_config/default_initial_connect_string.c
+  src/core/ext/client_config/http_connect_handshaker.c
   src/core/ext/client_config/initial_connect_string.c
   src/core/ext/client_config/lb_policy.c
   src/core/ext/client_config/lb_policy_factory.c
@@ -434,14 +449,15 @@
   src/core/ext/client_config/resolver.c
   src/core/ext/client_config/resolver_factory.c
   src/core/ext/client_config/resolver_registry.c
+  src/core/ext/client_config/resolver_result.c
   src/core/ext/client_config/subchannel.c
-  src/core/ext/client_config/subchannel_call_holder.c
   src/core/ext/client_config/subchannel_index.c
   src/core/ext/client_config/uri_parser.c
   src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
   src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c
   src/core/ext/transport/chttp2/client/insecure/channel_create.c
   src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
+  src/core/ext/lb_policy/grpclb/grpclb.c
   src/core/ext/lb_policy/grpclb/load_balancer_api.c
   src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
   third_party/nanopb/pb_common.c
@@ -453,8 +469,10 @@
   src/core/ext/resolver/sockaddr/sockaddr_resolver.c
   src/core/ext/load_reporting/load_reporting.c
   src/core/ext/load_reporting/load_reporting_filter.c
+  src/core/ext/census/base_resources.c
   src/core/ext/census/context.c
   src/core/ext/census/gen/census.pb.c
+  src/core/ext/census/gen/trace_context.pb.c
   src/core/ext/census/grpc_context.c
   src/core/ext/census/grpc_filter.c
   src/core/ext/census/grpc_plugin.c
@@ -462,6 +480,8 @@
   src/core/ext/census/mlog.c
   src/core/ext/census/operation.c
   src/core/ext/census/placeholders.c
+  src/core/ext/census/resource.c
+  src/core/ext/census/trace_context.c
   src/core/ext/census/tracing.c
   src/core/plugin_registry/grpc_plugin_registry.c
 )
@@ -488,30 +508,26 @@
   include/grpc/compression.h
   include/grpc/grpc.h
   include/grpc/grpc_posix.h
+  include/grpc/grpc_security_constants.h
   include/grpc/status.h
-  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
   include/grpc/impl/codegen/grpc_types.h
   include/grpc/impl/codegen/propagation_bits.h
   include/grpc/impl/codegen/status.h
-  include/grpc/impl/codegen/alloc.h
   include/grpc/impl/codegen/atm.h
   include/grpc/impl/codegen/atm_gcc_atomic.h
   include/grpc/impl/codegen/atm_gcc_sync.h
   include/grpc/impl/codegen/atm_windows.h
-  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/gpr_types.h
   include/grpc/impl/codegen/port_platform.h
   include/grpc/impl/codegen/slice.h
-  include/grpc/impl/codegen/slice_buffer.h
   include/grpc/impl/codegen/sync.h
   include/grpc/impl/codegen/sync_generic.h
   include/grpc/impl/codegen/sync_posix.h
   include/grpc/impl/codegen/sync_windows.h
-  include/grpc/impl/codegen/time.h
   include/grpc/grpc_security.h
-  include/grpc/grpc_security_constants.h
   include/grpc/census.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
@@ -522,11 +538,13 @@
 endforeach()
 
   
-install(TARGETS grpc EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
   
 add_library(grpc_cronet
@@ -536,9 +554,11 @@
   src/core/lib/channel/channel_stack_builder.c
   src/core/lib/channel/compress_filter.c
   src/core/lib/channel/connected_channel.c
+  src/core/lib/channel/deadline_filter.c
   src/core/lib/channel/handshaker.c
   src/core/lib/channel/http_client_filter.c
   src/core/lib/channel/http_server_filter.c
+  src/core/lib/channel/message_size_filter.c
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/debug/trace.c
@@ -546,6 +566,7 @@
   src/core/lib/http/httpcli.c
   src/core/lib/http/parser.c
   src/core/lib/iomgr/closure.c
+  src/core/lib/iomgr/combiner.c
   src/core/lib/iomgr/endpoint.c
   src/core/lib/iomgr/endpoint_pair_posix.c
   src/core/lib/iomgr/endpoint_pair_windows.c
@@ -617,6 +638,7 @@
   src/core/lib/transport/metadata.c
   src/core/lib/transport/metadata_batch.c
   src/core/lib/transport/static_metadata.c
+  src/core/lib/transport/timeout_encoding.c
   src/core/lib/transport/transport.c
   src/core/lib/transport/transport_op_string.c
   src/core/ext/transport/cronet/client/secure/cronet_channel_create.c
@@ -642,17 +664,16 @@
   src/core/ext/transport/chttp2/transport/status_conversion.c
   src/core/ext/transport/chttp2/transport/stream_lists.c
   src/core/ext/transport/chttp2/transport/stream_map.c
-  src/core/ext/transport/chttp2/transport/timeout_encoding.c
   src/core/ext/transport/chttp2/transport/varint.c
   src/core/ext/transport/chttp2/transport/writing.c
   src/core/ext/transport/chttp2/alpn/alpn.c
   src/core/ext/client_config/channel_connectivity.c
   src/core/ext/client_config/client_channel.c
   src/core/ext/client_config/client_channel_factory.c
-  src/core/ext/client_config/client_config.c
   src/core/ext/client_config/client_config_plugin.c
   src/core/ext/client_config/connector.c
   src/core/ext/client_config/default_initial_connect_string.c
+  src/core/ext/client_config/http_connect_handshaker.c
   src/core/ext/client_config/initial_connect_string.c
   src/core/ext/client_config/lb_policy.c
   src/core/ext/client_config/lb_policy_factory.c
@@ -661,8 +682,8 @@
   src/core/ext/client_config/resolver.c
   src/core/ext/client_config/resolver_factory.c
   src/core/ext/client_config/resolver_registry.c
+  src/core/ext/client_config/resolver_result.c
   src/core/ext/client_config/subchannel.c
-  src/core/ext/client_config/subchannel_call_holder.c
   src/core/ext/client_config/subchannel_index.c
   src/core/ext/client_config/uri_parser.c
   src/core/lib/http/httpcli_security_connector.c
@@ -717,31 +738,27 @@
   include/grpc/compression.h
   include/grpc/grpc.h
   include/grpc/grpc_posix.h
+  include/grpc/grpc_security_constants.h
   include/grpc/status.h
-  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
   include/grpc/impl/codegen/grpc_types.h
   include/grpc/impl/codegen/propagation_bits.h
   include/grpc/impl/codegen/status.h
-  include/grpc/impl/codegen/alloc.h
   include/grpc/impl/codegen/atm.h
   include/grpc/impl/codegen/atm_gcc_atomic.h
   include/grpc/impl/codegen/atm_gcc_sync.h
   include/grpc/impl/codegen/atm_windows.h
-  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/gpr_types.h
   include/grpc/impl/codegen/port_platform.h
   include/grpc/impl/codegen/slice.h
-  include/grpc/impl/codegen/slice_buffer.h
   include/grpc/impl/codegen/sync.h
   include/grpc/impl/codegen/sync_generic.h
   include/grpc/impl/codegen/sync_posix.h
   include/grpc/impl/codegen/sync_windows.h
-  include/grpc/impl/codegen/time.h
   include/grpc/grpc_cronet.h
   include/grpc/grpc_security.h
-  include/grpc/grpc_security_constants.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -751,11 +768,13 @@
 endforeach()
 
   
-install(TARGETS grpc_cronet EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc_cronet EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
   
 add_library(grpc_unsecure
@@ -766,9 +785,11 @@
   src/core/lib/channel/channel_stack_builder.c
   src/core/lib/channel/compress_filter.c
   src/core/lib/channel/connected_channel.c
+  src/core/lib/channel/deadline_filter.c
   src/core/lib/channel/handshaker.c
   src/core/lib/channel/http_client_filter.c
   src/core/lib/channel/http_server_filter.c
+  src/core/lib/channel/message_size_filter.c
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/debug/trace.c
@@ -776,6 +797,7 @@
   src/core/lib/http/httpcli.c
   src/core/lib/http/parser.c
   src/core/lib/iomgr/closure.c
+  src/core/lib/iomgr/combiner.c
   src/core/lib/iomgr/endpoint.c
   src/core/lib/iomgr/endpoint_pair_posix.c
   src/core/lib/iomgr/endpoint_pair_windows.c
@@ -847,6 +869,7 @@
   src/core/lib/transport/metadata.c
   src/core/lib/transport/metadata_batch.c
   src/core/lib/transport/static_metadata.c
+  src/core/lib/transport/timeout_encoding.c
   src/core/lib/transport/transport.c
   src/core/lib/transport/transport_op_string.c
   src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
@@ -870,7 +893,6 @@
   src/core/ext/transport/chttp2/transport/status_conversion.c
   src/core/ext/transport/chttp2/transport/stream_lists.c
   src/core/ext/transport/chttp2/transport/stream_map.c
-  src/core/ext/transport/chttp2/transport/timeout_encoding.c
   src/core/ext/transport/chttp2/transport/varint.c
   src/core/ext/transport/chttp2/transport/writing.c
   src/core/ext/transport/chttp2/alpn/alpn.c
@@ -879,10 +901,10 @@
   src/core/ext/client_config/channel_connectivity.c
   src/core/ext/client_config/client_channel.c
   src/core/ext/client_config/client_channel_factory.c
-  src/core/ext/client_config/client_config.c
   src/core/ext/client_config/client_config_plugin.c
   src/core/ext/client_config/connector.c
   src/core/ext/client_config/default_initial_connect_string.c
+  src/core/ext/client_config/http_connect_handshaker.c
   src/core/ext/client_config/initial_connect_string.c
   src/core/ext/client_config/lb_policy.c
   src/core/ext/client_config/lb_policy_factory.c
@@ -891,14 +913,15 @@
   src/core/ext/client_config/resolver.c
   src/core/ext/client_config/resolver_factory.c
   src/core/ext/client_config/resolver_registry.c
+  src/core/ext/client_config/resolver_result.c
   src/core/ext/client_config/subchannel.c
-  src/core/ext/client_config/subchannel_call_holder.c
   src/core/ext/client_config/subchannel_index.c
   src/core/ext/client_config/uri_parser.c
   src/core/ext/resolver/dns/native/dns_resolver.c
   src/core/ext/resolver/sockaddr/sockaddr_resolver.c
   src/core/ext/load_reporting/load_reporting.c
   src/core/ext/load_reporting/load_reporting_filter.c
+  src/core/ext/lb_policy/grpclb/grpclb.c
   src/core/ext/lb_policy/grpclb/load_balancer_api.c
   src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
   third_party/nanopb/pb_common.c
@@ -906,8 +929,10 @@
   third_party/nanopb/pb_encode.c
   src/core/ext/lb_policy/pick_first/pick_first.c
   src/core/ext/lb_policy/round_robin/round_robin.c
+  src/core/ext/census/base_resources.c
   src/core/ext/census/context.c
   src/core/ext/census/gen/census.pb.c
+  src/core/ext/census/gen/trace_context.pb.c
   src/core/ext/census/grpc_context.c
   src/core/ext/census/grpc_filter.c
   src/core/ext/census/grpc_plugin.c
@@ -915,6 +940,8 @@
   src/core/ext/census/mlog.c
   src/core/ext/census/operation.c
   src/core/ext/census/placeholders.c
+  src/core/ext/census/resource.c
+  src/core/ext/census/trace_context.c
   src/core/ext/census/tracing.c
   src/core/plugin_registry/grpc_unsecure_plugin_registry.c
 )
@@ -939,28 +966,25 @@
   include/grpc/compression.h
   include/grpc/grpc.h
   include/grpc/grpc_posix.h
+  include/grpc/grpc_security_constants.h
   include/grpc/status.h
-  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
   include/grpc/impl/codegen/grpc_types.h
   include/grpc/impl/codegen/propagation_bits.h
   include/grpc/impl/codegen/status.h
-  include/grpc/impl/codegen/alloc.h
   include/grpc/impl/codegen/atm.h
   include/grpc/impl/codegen/atm_gcc_atomic.h
   include/grpc/impl/codegen/atm_gcc_sync.h
   include/grpc/impl/codegen/atm_windows.h
-  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/gpr_types.h
   include/grpc/impl/codegen/port_platform.h
   include/grpc/impl/codegen/slice.h
-  include/grpc/impl/codegen/slice_buffer.h
   include/grpc/impl/codegen/sync.h
   include/grpc/impl/codegen/sync_generic.h
   include/grpc/impl/codegen/sync_posix.h
   include/grpc/impl/codegen/sync_windows.h
-  include/grpc/impl/codegen/time.h
   include/grpc/census.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
@@ -971,46 +995,49 @@
 endforeach()
 
   
-install(TARGETS grpc_unsecure EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc_unsecure EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
   
 add_library(grpc++
+  src/cpp/client/insecure_credentials.cc
   src/cpp/client/secure_credentials.cc
   src/cpp/common/auth_property_iterator.cc
   src/cpp/common/secure_auth_context.cc
   src/cpp/common/secure_channel_arguments.cc
   src/cpp/common/secure_create_auth_context.cc
+  src/cpp/server/insecure_server_credentials.cc
   src/cpp/server/secure_server_credentials.cc
-  src/cpp/client/channel.cc
+  src/cpp/client/channel_cc.cc
   src/cpp/client/client_context.cc
   src/cpp/client/create_channel.cc
   src/cpp/client/create_channel_internal.cc
   src/cpp/client/create_channel_posix.cc
-  src/cpp/client/credentials.cc
+  src/cpp/client/credentials_cc.cc
   src/cpp/client/generic_stub.cc
-  src/cpp/client/insecure_credentials.cc
   src/cpp/common/channel_arguments.cc
-  src/cpp/common/completion_queue.cc
+  src/cpp/common/channel_filter.cc
+  src/cpp/common/completion_queue_cc.cc
   src/cpp/common/core_codegen.cc
   src/cpp/common/rpc_method.cc
   src/cpp/server/async_generic_service.cc
   src/cpp/server/create_default_thread_pool.cc
   src/cpp/server/dynamic_thread_pool.cc
-  src/cpp/server/insecure_server_credentials.cc
-  src/cpp/server/server.cc
   src/cpp/server/server_builder.cc
+  src/cpp/server/server_cc.cc
   src/cpp/server/server_context.cc
   src/cpp/server/server_credentials.cc
   src/cpp/server/server_posix.cc
-  src/cpp/util/byte_buffer.cc
-  src/cpp/util/slice.cc
+  src/cpp/util/byte_buffer_cc.cc
+  src/cpp/util/slice_cc.cc
   src/cpp/util/status.cc
   src/cpp/util/string_ref.cc
-  src/cpp/util/time.cc
+  src/cpp/util/time_cc.cc
   src/cpp/codegen/codegen_init.cc
 )
 
@@ -1101,6 +1128,7 @@
   include/grpc++/impl/codegen/service_type.h
   include/grpc++/impl/codegen/status.h
   include/grpc++/impl/codegen/status_code_enum.h
+  include/grpc++/impl/codegen/status_helper.h
   include/grpc++/impl/codegen/string_ref.h
   include/grpc++/impl/codegen/stub_options.h
   include/grpc++/impl/codegen/sync.h
@@ -1108,27 +1136,23 @@
   include/grpc++/impl/codegen/sync_no_cxx11.h
   include/grpc++/impl/codegen/sync_stream.h
   include/grpc++/impl/codegen/time.h
-  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
   include/grpc/impl/codegen/grpc_types.h
   include/grpc/impl/codegen/propagation_bits.h
   include/grpc/impl/codegen/status.h
-  include/grpc/impl/codegen/alloc.h
   include/grpc/impl/codegen/atm.h
   include/grpc/impl/codegen/atm_gcc_atomic.h
   include/grpc/impl/codegen/atm_gcc_sync.h
   include/grpc/impl/codegen/atm_windows.h
-  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/gpr_types.h
   include/grpc/impl/codegen/port_platform.h
   include/grpc/impl/codegen/slice.h
-  include/grpc/impl/codegen/slice_buffer.h
   include/grpc/impl/codegen/sync.h
   include/grpc/impl/codegen/sync_generic.h
   include/grpc/impl/codegen/sync_posix.h
   include/grpc/impl/codegen/sync_windows.h
-  include/grpc/impl/codegen/time.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -1138,11 +1162,13 @@
 endforeach()
 
   
-install(TARGETS grpc++ EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc++ EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
   
 add_library(grpc++_reflection
@@ -1193,6 +1219,7 @@
   include/grpc++/impl/codegen/service_type.h
   include/grpc++/impl/codegen/status.h
   include/grpc++/impl/codegen/status_code_enum.h
+  include/grpc++/impl/codegen/status_helper.h
   include/grpc++/impl/codegen/string_ref.h
   include/grpc++/impl/codegen/stub_options.h
   include/grpc++/impl/codegen/sync.h
@@ -1200,27 +1227,23 @@
   include/grpc++/impl/codegen/sync_no_cxx11.h
   include/grpc++/impl/codegen/sync_stream.h
   include/grpc++/impl/codegen/time.h
-  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
   include/grpc/impl/codegen/grpc_types.h
   include/grpc/impl/codegen/propagation_bits.h
   include/grpc/impl/codegen/status.h
-  include/grpc/impl/codegen/alloc.h
   include/grpc/impl/codegen/atm.h
   include/grpc/impl/codegen/atm_gcc_atomic.h
   include/grpc/impl/codegen/atm_gcc_sync.h
   include/grpc/impl/codegen/atm_windows.h
-  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/gpr_types.h
   include/grpc/impl/codegen/port_platform.h
   include/grpc/impl/codegen/slice.h
-  include/grpc/impl/codegen/slice_buffer.h
   include/grpc/impl/codegen/sync.h
   include/grpc/impl/codegen/sync_generic.h
   include/grpc/impl/codegen/sync_posix.h
   include/grpc/impl/codegen/sync_windows.h
-  include/grpc/impl/codegen/time.h
   include/grpc++/impl/codegen/config_protobuf.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
@@ -1231,41 +1254,44 @@
 endforeach()
 
   
-install(TARGETS grpc++_reflection EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc++_reflection EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
   
 add_library(grpc++_unsecure
+  src/cpp/client/insecure_credentials.cc
   src/cpp/common/insecure_create_auth_context.cc
-  src/cpp/client/channel.cc
+  src/cpp/server/insecure_server_credentials.cc
+  src/cpp/client/channel_cc.cc
   src/cpp/client/client_context.cc
   src/cpp/client/create_channel.cc
   src/cpp/client/create_channel_internal.cc
   src/cpp/client/create_channel_posix.cc
-  src/cpp/client/credentials.cc
+  src/cpp/client/credentials_cc.cc
   src/cpp/client/generic_stub.cc
-  src/cpp/client/insecure_credentials.cc
   src/cpp/common/channel_arguments.cc
-  src/cpp/common/completion_queue.cc
+  src/cpp/common/channel_filter.cc
+  src/cpp/common/completion_queue_cc.cc
   src/cpp/common/core_codegen.cc
   src/cpp/common/rpc_method.cc
   src/cpp/server/async_generic_service.cc
   src/cpp/server/create_default_thread_pool.cc
   src/cpp/server/dynamic_thread_pool.cc
-  src/cpp/server/insecure_server_credentials.cc
-  src/cpp/server/server.cc
   src/cpp/server/server_builder.cc
+  src/cpp/server/server_cc.cc
   src/cpp/server/server_context.cc
   src/cpp/server/server_credentials.cc
   src/cpp/server/server_posix.cc
-  src/cpp/util/byte_buffer.cc
-  src/cpp/util/slice.cc
+  src/cpp/util/byte_buffer_cc.cc
+  src/cpp/util/slice_cc.cc
   src/cpp/util/status.cc
   src/cpp/util/string_ref.cc
-  src/cpp/util/time.cc
+  src/cpp/util/time_cc.cc
   src/cpp/codegen/codegen_init.cc
 )
 
@@ -1283,7 +1309,6 @@
   ${_gRPC_PROTOBUF_LIBRARIES}
   gpr
   grpc_unsecure
-  grpc
 )
 
 foreach(_hdr
@@ -1357,6 +1382,7 @@
   include/grpc++/impl/codegen/service_type.h
   include/grpc++/impl/codegen/status.h
   include/grpc++/impl/codegen/status_code_enum.h
+  include/grpc++/impl/codegen/status_helper.h
   include/grpc++/impl/codegen/string_ref.h
   include/grpc++/impl/codegen/stub_options.h
   include/grpc++/impl/codegen/sync.h
@@ -1364,27 +1390,23 @@
   include/grpc++/impl/codegen/sync_no_cxx11.h
   include/grpc++/impl/codegen/sync_stream.h
   include/grpc++/impl/codegen/time.h
-  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
   include/grpc/impl/codegen/grpc_types.h
   include/grpc/impl/codegen/propagation_bits.h
   include/grpc/impl/codegen/status.h
-  include/grpc/impl/codegen/alloc.h
   include/grpc/impl/codegen/atm.h
   include/grpc/impl/codegen/atm_gcc_atomic.h
   include/grpc/impl/codegen/atm_gcc_sync.h
   include/grpc/impl/codegen/atm_windows.h
-  include/grpc/impl/codegen/log.h
+  include/grpc/impl/codegen/gpr_types.h
   include/grpc/impl/codegen/port_platform.h
   include/grpc/impl/codegen/slice.h
-  include/grpc/impl/codegen/slice_buffer.h
   include/grpc/impl/codegen/sync.h
   include/grpc/impl/codegen/sync_generic.h
   include/grpc/impl/codegen/sync_posix.h
   include/grpc/impl/codegen/sync_windows.h
-  include/grpc/impl/codegen/time.h
 )
   string(REPLACE "include/" "" _path ${_hdr})
   get_filename_component(_path ${_path} PATH)
@@ -1394,11 +1416,13 @@
 endforeach()
 
   
-install(TARGETS grpc++_unsecure EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc++_unsecure EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
   
 add_library(grpc_plugin_support
@@ -1406,6 +1430,7 @@
   src/compiler/csharp_generator.cc
   src/compiler/node_generator.cc
   src/compiler/objective_c_generator.cc
+  src/compiler/php_generator.cc
   src/compiler/python_generator.cc
   src/compiler/ruby_generator.cc
 )
@@ -1434,11 +1459,13 @@
 endforeach()
 
   
-install(TARGETS grpc_plugin_support EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc_plugin_support EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
   
 add_library(grpc_csharp_ext
@@ -1461,11 +1488,13 @@
 
 
   
-install(TARGETS grpc_csharp_ext EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc_csharp_ext EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
 
 
@@ -1488,11 +1517,13 @@
 )
 
 
-install(TARGETS gen_hpack_tables EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS gen_hpack_tables EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
 
 add_executable(gen_legal_metadata_characters
@@ -1510,12 +1541,38 @@
 
 
 
-install(TARGETS gen_legal_metadata_characters EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+if (gRPC_INSTALL)
+  install(TARGETS gen_legal_metadata_characters EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
+
+
+add_executable(gen_percent_encoding_tables
+  tools/codegen/core/gen_percent_encoding_tables.c
 )
 
+target_include_directories(gen_percent_encoding_tables
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${BORINGSSL_ROOT_DIR}/include
+  PRIVATE ${PROTOBUF_ROOT_DIR}/src
+  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
+)
+
+
+
+if (gRPC_INSTALL)
+  install(TARGETS gen_percent_encoding_tables EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
+
 
 add_executable(grpc_create_jwt
   test/core/security/create_jwt.c
@@ -1537,11 +1594,13 @@
 )
 
 
-install(TARGETS grpc_create_jwt EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc_create_jwt EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
 
 add_executable(grpc_print_google_default_creds_token
@@ -1563,11 +1622,13 @@
 )
 
 
-install(TARGETS grpc_print_google_default_creds_token EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc_print_google_default_creds_token EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
 
 add_executable(grpc_verify_jwt
@@ -1589,11 +1650,13 @@
 )
 
 
-install(TARGETS grpc_verify_jwt EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc_verify_jwt EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
 
 add_executable(grpc_cpp_plugin
@@ -1615,11 +1678,13 @@
 )
 
 
-install(TARGETS grpc_cpp_plugin EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc_cpp_plugin EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
 
 add_executable(grpc_csharp_plugin
@@ -1641,11 +1706,13 @@
 )
 
 
-install(TARGETS grpc_csharp_plugin EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc_csharp_plugin EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
 
 add_executable(grpc_node_plugin
@@ -1667,11 +1734,13 @@
 )
 
 
-install(TARGETS grpc_node_plugin EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc_node_plugin EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
 
 add_executable(grpc_objective_c_plugin
@@ -1693,12 +1762,42 @@
 )
 
 
-install(TARGETS grpc_objective_c_plugin EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+if (gRPC_INSTALL)
+  install(TARGETS grpc_objective_c_plugin EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
+
+
+add_executable(grpc_php_plugin
+  src/compiler/php_plugin.cc
 )
 
+target_include_directories(grpc_php_plugin
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${BORINGSSL_ROOT_DIR}/include
+  PRIVATE ${PROTOBUF_ROOT_DIR}/src
+  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
+)
+
+target_link_libraries(grpc_php_plugin
+  ${_gRPC_PROTOBUF_PROTOC_LIBRARIES}
+  grpc_plugin_support
+)
+
+
+if (gRPC_INSTALL)
+  install(TARGETS grpc_php_plugin EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
+
 
 add_executable(grpc_python_plugin
   src/compiler/python_plugin.cc
@@ -1719,11 +1818,13 @@
 )
 
 
-install(TARGETS grpc_python_plugin EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc_python_plugin EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
 
 add_executable(grpc_ruby_plugin
@@ -1745,11 +1846,13 @@
 )
 
 
-install(TARGETS grpc_ruby_plugin EXPORT gRPCTargets
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
+if (gRPC_INSTALL)
+  install(TARGETS grpc_ruby_plugin EXPORT gRPCTargets
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  )
+endif()
 
 
 
@@ -1758,10 +1861,12 @@
 
 
 
-install(EXPORT gRPCTargets
-  DESTINATION ${CMAKE_INSTALL_CMAKEDIR}
-  NAMESPACE gRPC::
-)
+if (gRPC_INSTALL)
+  install(EXPORT gRPCTargets
+    DESTINATION ${CMAKE_INSTALL_CMAKEDIR}
+    NAMESPACE gRPC::
+  )
+endif()
 
 foreach(_config gRPCConfig gRPCConfigVersion)
   configure_file(tools/cmake/${_config}.cmake.in
diff --git a/INSTALL.md b/INSTALL.md
index 6718aad..6861455 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -9,7 +9,7 @@
  * [Java](https://github.com/grpc/grpc-java)
  * [Node](src/node): `npm install grpc`
  * [Objective-C](src/objective-c)
- * [PHP](src/php): `pecl install grpc-beta`
+ * [PHP](src/php): `pecl install grpc`
  * [Python](src/python/grpcio): `pip install grpcio`
  * [Ruby](src/ruby): `gem install grpc`
 
@@ -55,3 +55,55 @@
  $ make
  $ [sudo] make install
 ```
+
+##Windows
+
+There are several ways to build under Windows, of varying complexity depending
+on experience with the tools involved.
+
+<!--
+###Visual Studio
+
+Versions 2013 and 2015 are both supported. You can use [their respective
+community
+editions](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx).
+
+Building the C Core:
+- Open [grpc.sln](https://github.com/grpc/grpc/blob/master/vsprojects/grpc.sln).
+- Select your build target.
+- Build the `grpc` project.
+
+Building the C++ runtime:
+- You need [CMake](https://cmake.org/) on your path to build protobuf (see below
+  for building using solely CMake).
+- Run `vsprojects/build_protos.bat` (needs `cmake.exe` in your path).
+- Open [buildtests_cxx.sln]()
+- Select your build target.
+- build the `grpc++` project.
+-->
+
+###msys2
+
+This approach requires having [msys2](https://msys2.github.io/) installed.
+
+- The Makefile (and source code) should support msys2's mingw32 and mingw64
+  compilers. Building with msys2's native compiler is also possible, but
+  difficult.
+- The Makefile is expecting the Windows versions of OpenSSL (see
+  https://slproweb.com/products/Win32OpenSSL.html). It's also possible to build
+  the Windows version of OpenSSL from scratch. The output should be `libeay32`
+  and `ssleay32`.
+- If you are not installing the above files under msys2's path, you may specify
+  it, for instance, in the following way:
+  ```CPPFLAGS=”-I/c/OpenSSL-Win32/include” LDFLAGS=”-L/c/OpenSSL-Win32/lib” make static_c```
+- [protobuf3](https://github.com/google/protobuf/blob/master/src/README.md#c-installation---windows)
+  must be installed on the msys2 path.
+
+###Cmake (experimental)
+
+- Install [CMake](https://cmake.org/download/).
+- Run it over [grpc's
+  CMakeLists.txt](https://github.com/grpc/grpc/blob/master/CMakeLists.txt) to
+  generate "projects" for your compiler.
+- Build with your compiler of choice. The generated build files should have the
+  protobuf3 dependency baked in.
diff --git a/Makefile b/Makefile
index 8b6114b..62c6582 100644
--- a/Makefile
+++ b/Makefile
@@ -492,7 +492,7 @@
 OPENSSL_ALPN_CHECK_CMD = $(PKG_CONFIG) --atleast-version=1.0.2 openssl
 OPENSSL_NPN_CHECK_CMD = $(PKG_CONFIG) --atleast-version=1.0.1 openssl
 ZLIB_CHECK_CMD = $(PKG_CONFIG) --exists zlib
-PROTOBUF_CHECK_CMD = $(PKG_CONFIG) --atleast-version=3.0.0-alpha-3 protobuf
+PROTOBUF_CHECK_CMD = $(PKG_CONFIG) --atleast-version=3.0.0 protobuf
 else # HAS_PKG_CONFIG
 
 ifeq ($(SYSTEM),MINGW32)
@@ -732,7 +732,7 @@
 
 CPPFLAGS := -Ithird_party/googletest/include $(CPPFLAGS)
 
-PROTOC_PLUGINS_ALL = $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(BINDIR)/$(CONFIG)/grpc_csharp_plugin $(BINDIR)/$(CONFIG)/grpc_node_plugin $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin $(BINDIR)/$(CONFIG)/grpc_python_plugin $(BINDIR)/$(CONFIG)/grpc_ruby_plugin
+PROTOC_PLUGINS_ALL = $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(BINDIR)/$(CONFIG)/grpc_csharp_plugin $(BINDIR)/$(CONFIG)/grpc_node_plugin $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin $(BINDIR)/$(CONFIG)/grpc_php_plugin $(BINDIR)/$(CONFIG)/grpc_python_plugin $(BINDIR)/$(CONFIG)/grpc_ruby_plugin
 PROTOC_PLUGINS_DIR = $(BINDIR)/$(CONFIG)
 
 ifeq ($(HAS_SYSTEM_PROTOBUF),true)
@@ -800,13 +800,6 @@
 NO_DEPS = true
 endif
 
-INSTALL_OK = false
-ifeq ($(HAS_VALID_PROTOC),true)
-ifeq ($(HAS_SYSTEM_PROTOBUF_VERIFY),true)
-INSTALL_OK = true
-endif
-endif
-
 .SECONDARY = %.pb.h %.pb.cc
 
 ifeq ($(DEP_MISSING),)
@@ -915,14 +908,18 @@
 bin_decoder_test: $(BINDIR)/$(CONFIG)/bin_decoder_test
 bin_encoder_test: $(BINDIR)/$(CONFIG)/bin_encoder_test
 census_context_test: $(BINDIR)/$(CONFIG)/census_context_test
+census_resource_test: $(BINDIR)/$(CONFIG)/census_resource_test
+census_trace_context_test: $(BINDIR)/$(CONFIG)/census_trace_context_test
 channel_create_test: $(BINDIR)/$(CONFIG)/channel_create_test
 chttp2_hpack_encoder_test: $(BINDIR)/$(CONFIG)/chttp2_hpack_encoder_test
 chttp2_status_conversion_test: $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test
 chttp2_stream_map_test: $(BINDIR)/$(CONFIG)/chttp2_stream_map_test
 chttp2_varint_test: $(BINDIR)/$(CONFIG)/chttp2_varint_test
 client_fuzzer: $(BINDIR)/$(CONFIG)/client_fuzzer
+combiner_test: $(BINDIR)/$(CONFIG)/combiner_test
 compression_test: $(BINDIR)/$(CONFIG)/compression_test
 concurrent_connectivity_test: $(BINDIR)/$(CONFIG)/concurrent_connectivity_test
+connection_refused_test: $(BINDIR)/$(CONFIG)/connection_refused_test
 dns_resolver_connectivity_test: $(BINDIR)/$(CONFIG)/dns_resolver_connectivity_test
 dns_resolver_test: $(BINDIR)/$(CONFIG)/dns_resolver_test
 dualstack_socket_test: $(BINDIR)/$(CONFIG)/dualstack_socket_test
@@ -936,6 +933,7 @@
 fling_test: $(BINDIR)/$(CONFIG)/fling_test
 gen_hpack_tables: $(BINDIR)/$(CONFIG)/gen_hpack_tables
 gen_legal_metadata_characters: $(BINDIR)/$(CONFIG)/gen_legal_metadata_characters
+gen_percent_encoding_tables: $(BINDIR)/$(CONFIG)/gen_percent_encoding_tables
 goaway_server_test: $(BINDIR)/$(CONFIG)/goaway_server_test
 gpr_avl_test: $(BINDIR)/$(CONFIG)/gpr_avl_test
 gpr_backoff_test: $(BINDIR)/$(CONFIG)/gpr_backoff_test
@@ -945,6 +943,8 @@
 gpr_histogram_test: $(BINDIR)/$(CONFIG)/gpr_histogram_test
 gpr_host_port_test: $(BINDIR)/$(CONFIG)/gpr_host_port_test
 gpr_log_test: $(BINDIR)/$(CONFIG)/gpr_log_test
+gpr_mpscq_test: $(BINDIR)/$(CONFIG)/gpr_mpscq_test
+gpr_percent_encoding_test: $(BINDIR)/$(CONFIG)/gpr_percent_encoding_test
 gpr_slice_buffer_test: $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test
 gpr_slice_test: $(BINDIR)/$(CONFIG)/gpr_slice_test
 gpr_stack_lockfree_test: $(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test
@@ -999,6 +999,8 @@
 nanopb_fuzzer_response_test: $(BINDIR)/$(CONFIG)/nanopb_fuzzer_response_test
 nanopb_fuzzer_serverlist_test: $(BINDIR)/$(CONFIG)/nanopb_fuzzer_serverlist_test
 no_server_test: $(BINDIR)/$(CONFIG)/no_server_test
+percent_decode_fuzzer: $(BINDIR)/$(CONFIG)/percent_decode_fuzzer
+percent_encode_fuzzer: $(BINDIR)/$(CONFIG)/percent_encode_fuzzer
 resolve_address_test: $(BINDIR)/$(CONFIG)/resolve_address_test
 secure_channel_create_test: $(BINDIR)/$(CONFIG)/secure_channel_create_test
 secure_endpoint_test: $(BINDIR)/$(CONFIG)/secure_endpoint_test
@@ -1038,6 +1040,7 @@
 cxx_string_ref_test: $(BINDIR)/$(CONFIG)/cxx_string_ref_test
 cxx_time_test: $(BINDIR)/$(CONFIG)/cxx_time_test
 end2end_test: $(BINDIR)/$(CONFIG)/end2end_test
+filter_end2end_test: $(BINDIR)/$(CONFIG)/filter_end2end_test
 generic_end2end_test: $(BINDIR)/$(CONFIG)/generic_end2end_test
 golden_file_test: $(BINDIR)/$(CONFIG)/golden_file_test
 grpc_cli: $(BINDIR)/$(CONFIG)/grpc_cli
@@ -1045,9 +1048,12 @@
 grpc_csharp_plugin: $(BINDIR)/$(CONFIG)/grpc_csharp_plugin
 grpc_node_plugin: $(BINDIR)/$(CONFIG)/grpc_node_plugin
 grpc_objective_c_plugin: $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin
+grpc_php_plugin: $(BINDIR)/$(CONFIG)/grpc_php_plugin
 grpc_python_plugin: $(BINDIR)/$(CONFIG)/grpc_python_plugin
 grpc_ruby_plugin: $(BINDIR)/$(CONFIG)/grpc_ruby_plugin
+grpc_tool_test: $(BINDIR)/$(CONFIG)/grpc_tool_test
 grpclb_api_test: $(BINDIR)/$(CONFIG)/grpclb_api_test
+grpclb_test: $(BINDIR)/$(CONFIG)/grpclb_test
 hybrid_end2end_test: $(BINDIR)/$(CONFIG)/hybrid_end2end_test
 interop_client: $(BINDIR)/$(CONFIG)/interop_client
 interop_server: $(BINDIR)/$(CONFIG)/interop_server
@@ -1121,9 +1127,7 @@
 simple_request_bad_client_test: $(BINDIR)/$(CONFIG)/simple_request_bad_client_test
 unknown_frame_bad_client_test: $(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test
 window_overflow_bad_client_test: $(BINDIR)/$(CONFIG)/window_overflow_bad_client_test
-bad_ssl_alpn_server: $(BINDIR)/$(CONFIG)/bad_ssl_alpn_server
 bad_ssl_cert_server: $(BINDIR)/$(CONFIG)/bad_ssl_cert_server
-bad_ssl_alpn_test: $(BINDIR)/$(CONFIG)/bad_ssl_alpn_test
 bad_ssl_cert_test: $(BINDIR)/$(CONFIG)/bad_ssl_cert_test
 h2_census_test: $(BINDIR)/$(CONFIG)/h2_census_test
 h2_compress_test: $(BINDIR)/$(CONFIG)/h2_compress_test
@@ -1132,7 +1136,8 @@
 h2_full_test: $(BINDIR)/$(CONFIG)/h2_full_test
 h2_full+pipe_test: $(BINDIR)/$(CONFIG)/h2_full+pipe_test
 h2_full+trace_test: $(BINDIR)/$(CONFIG)/h2_full+trace_test
-h2_loadreporting_test: $(BINDIR)/$(CONFIG)/h2_loadreporting_test
+h2_http_proxy_test: $(BINDIR)/$(CONFIG)/h2_http_proxy_test
+h2_load_reporting_test: $(BINDIR)/$(CONFIG)/h2_load_reporting_test
 h2_oauth2_test: $(BINDIR)/$(CONFIG)/h2_oauth2_test
 h2_proxy_test: $(BINDIR)/$(CONFIG)/h2_proxy_test
 h2_sockpair_test: $(BINDIR)/$(CONFIG)/h2_sockpair_test
@@ -1148,7 +1153,8 @@
 h2_full_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_nosec_test
 h2_full+pipe_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+pipe_nosec_test
 h2_full+trace_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+trace_nosec_test
-h2_loadreporting_nosec_test: $(BINDIR)/$(CONFIG)/h2_loadreporting_nosec_test
+h2_http_proxy_nosec_test: $(BINDIR)/$(CONFIG)/h2_http_proxy_nosec_test
+h2_load_reporting_nosec_test: $(BINDIR)/$(CONFIG)/h2_load_reporting_nosec_test
 h2_proxy_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_nosec_test
 h2_sockpair_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_nosec_test
 h2_sockpair+trace_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_nosec_test
@@ -1162,6 +1168,8 @@
 json_fuzzer_test_one_entry: $(BINDIR)/$(CONFIG)/json_fuzzer_test_one_entry
 nanopb_fuzzer_response_test_one_entry: $(BINDIR)/$(CONFIG)/nanopb_fuzzer_response_test_one_entry
 nanopb_fuzzer_serverlist_test_one_entry: $(BINDIR)/$(CONFIG)/nanopb_fuzzer_serverlist_test_one_entry
+percent_decode_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/percent_decode_fuzzer_one_entry
+percent_encode_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/percent_encode_fuzzer_one_entry
 server_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/server_fuzzer_one_entry
 uri_fuzzer_test_one_entry: $(BINDIR)/$(CONFIG)/uri_fuzzer_test_one_entry
 
@@ -1216,9 +1224,9 @@
 pc_cxx_unsecure: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc
 
 ifeq ($(EMBED_OPENSSL),true)
-privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_reflection_codegen.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a $(LIBDIR)/$(CONFIG)/libboringssl_aes_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_asn1_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_base64_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_bio_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_bn_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_bytestring_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_aead_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_cipher_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_cmac_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ed25519_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_x25519_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_dh_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_digest_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ec_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ecdsa_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_err_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_evp_extra_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_evp_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_pbkdf_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_hmac_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_pkcs12_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_pkcs8_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_poly1305_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_rsa_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_x509_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ssl_test_lib.a
+privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_reflection_codegen.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a $(LIBDIR)/$(CONFIG)/libboringssl_aes_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_asn1_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_base64_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_bio_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_bn_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_bytestring_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_aead_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_cipher_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_cmac_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ed25519_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_x25519_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_dh_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_digest_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ec_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ecdsa_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_err_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_evp_extra_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_evp_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_pbkdf_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_hmac_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_pkcs12_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_pkcs8_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_poly1305_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_rsa_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_x509_test_lib.a $(LIBDIR)/$(CONFIG)/libboringssl_ssl_test_lib.a
 else
-privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_reflection_codegen.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a
+privatelibs_cxx:  $(LIBDIR)/$(CONFIG)/libgrpc++_reflection_codegen.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a
 endif
 
 
@@ -1233,13 +1241,17 @@
   $(BINDIR)/$(CONFIG)/bin_decoder_test \
   $(BINDIR)/$(CONFIG)/bin_encoder_test \
   $(BINDIR)/$(CONFIG)/census_context_test \
+  $(BINDIR)/$(CONFIG)/census_resource_test \
+  $(BINDIR)/$(CONFIG)/census_trace_context_test \
   $(BINDIR)/$(CONFIG)/channel_create_test \
   $(BINDIR)/$(CONFIG)/chttp2_hpack_encoder_test \
   $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test \
   $(BINDIR)/$(CONFIG)/chttp2_stream_map_test \
   $(BINDIR)/$(CONFIG)/chttp2_varint_test \
+  $(BINDIR)/$(CONFIG)/combiner_test \
   $(BINDIR)/$(CONFIG)/compression_test \
   $(BINDIR)/$(CONFIG)/concurrent_connectivity_test \
+  $(BINDIR)/$(CONFIG)/connection_refused_test \
   $(BINDIR)/$(CONFIG)/dns_resolver_connectivity_test \
   $(BINDIR)/$(CONFIG)/dns_resolver_test \
   $(BINDIR)/$(CONFIG)/dualstack_socket_test \
@@ -1260,6 +1272,8 @@
   $(BINDIR)/$(CONFIG)/gpr_histogram_test \
   $(BINDIR)/$(CONFIG)/gpr_host_port_test \
   $(BINDIR)/$(CONFIG)/gpr_log_test \
+  $(BINDIR)/$(CONFIG)/gpr_mpscq_test \
+  $(BINDIR)/$(CONFIG)/gpr_percent_encoding_test \
   $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test \
   $(BINDIR)/$(CONFIG)/gpr_slice_test \
   $(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test \
@@ -1337,9 +1351,7 @@
   $(BINDIR)/$(CONFIG)/simple_request_bad_client_test \
   $(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test \
   $(BINDIR)/$(CONFIG)/window_overflow_bad_client_test \
-  $(BINDIR)/$(CONFIG)/bad_ssl_alpn_server \
   $(BINDIR)/$(CONFIG)/bad_ssl_cert_server \
-  $(BINDIR)/$(CONFIG)/bad_ssl_alpn_test \
   $(BINDIR)/$(CONFIG)/bad_ssl_cert_test \
   $(BINDIR)/$(CONFIG)/h2_census_test \
   $(BINDIR)/$(CONFIG)/h2_compress_test \
@@ -1348,7 +1360,8 @@
   $(BINDIR)/$(CONFIG)/h2_full_test \
   $(BINDIR)/$(CONFIG)/h2_full+pipe_test \
   $(BINDIR)/$(CONFIG)/h2_full+trace_test \
-  $(BINDIR)/$(CONFIG)/h2_loadreporting_test \
+  $(BINDIR)/$(CONFIG)/h2_http_proxy_test \
+  $(BINDIR)/$(CONFIG)/h2_load_reporting_test \
   $(BINDIR)/$(CONFIG)/h2_oauth2_test \
   $(BINDIR)/$(CONFIG)/h2_proxy_test \
   $(BINDIR)/$(CONFIG)/h2_sockpair_test \
@@ -1364,7 +1377,8 @@
   $(BINDIR)/$(CONFIG)/h2_full_nosec_test \
   $(BINDIR)/$(CONFIG)/h2_full+pipe_nosec_test \
   $(BINDIR)/$(CONFIG)/h2_full+trace_nosec_test \
-  $(BINDIR)/$(CONFIG)/h2_loadreporting_nosec_test \
+  $(BINDIR)/$(CONFIG)/h2_http_proxy_nosec_test \
+  $(BINDIR)/$(CONFIG)/h2_load_reporting_nosec_test \
   $(BINDIR)/$(CONFIG)/h2_proxy_nosec_test \
   $(BINDIR)/$(CONFIG)/h2_sockpair_nosec_test \
   $(BINDIR)/$(CONFIG)/h2_sockpair+trace_nosec_test \
@@ -1378,6 +1392,8 @@
   $(BINDIR)/$(CONFIG)/json_fuzzer_test_one_entry \
   $(BINDIR)/$(CONFIG)/nanopb_fuzzer_response_test_one_entry \
   $(BINDIR)/$(CONFIG)/nanopb_fuzzer_serverlist_test_one_entry \
+  $(BINDIR)/$(CONFIG)/percent_decode_fuzzer_one_entry \
+  $(BINDIR)/$(CONFIG)/percent_encode_fuzzer_one_entry \
   $(BINDIR)/$(CONFIG)/server_fuzzer_one_entry \
   $(BINDIR)/$(CONFIG)/uri_fuzzer_test_one_entry \
 
@@ -1399,10 +1415,13 @@
   $(BINDIR)/$(CONFIG)/cxx_string_ref_test \
   $(BINDIR)/$(CONFIG)/cxx_time_test \
   $(BINDIR)/$(CONFIG)/end2end_test \
+  $(BINDIR)/$(CONFIG)/filter_end2end_test \
   $(BINDIR)/$(CONFIG)/generic_end2end_test \
   $(BINDIR)/$(CONFIG)/golden_file_test \
   $(BINDIR)/$(CONFIG)/grpc_cli \
+  $(BINDIR)/$(CONFIG)/grpc_tool_test \
   $(BINDIR)/$(CONFIG)/grpclb_api_test \
+  $(BINDIR)/$(CONFIG)/grpclb_test \
   $(BINDIR)/$(CONFIG)/hybrid_end2end_test \
   $(BINDIR)/$(CONFIG)/interop_client \
   $(BINDIR)/$(CONFIG)/interop_server \
@@ -1483,10 +1502,13 @@
   $(BINDIR)/$(CONFIG)/cxx_string_ref_test \
   $(BINDIR)/$(CONFIG)/cxx_time_test \
   $(BINDIR)/$(CONFIG)/end2end_test \
+  $(BINDIR)/$(CONFIG)/filter_end2end_test \
   $(BINDIR)/$(CONFIG)/generic_end2end_test \
   $(BINDIR)/$(CONFIG)/golden_file_test \
   $(BINDIR)/$(CONFIG)/grpc_cli \
+  $(BINDIR)/$(CONFIG)/grpc_tool_test \
   $(BINDIR)/$(CONFIG)/grpclb_api_test \
+  $(BINDIR)/$(CONFIG)/grpclb_test \
   $(BINDIR)/$(CONFIG)/hybrid_end2end_test \
   $(BINDIR)/$(CONFIG)/interop_client \
   $(BINDIR)/$(CONFIG)/interop_server \
@@ -1536,6 +1558,10 @@
 	$(Q) $(BINDIR)/$(CONFIG)/bin_encoder_test || ( echo test bin_encoder_test failed ; exit 1 )
 	$(E) "[RUN]     Testing census_context_test"
 	$(Q) $(BINDIR)/$(CONFIG)/census_context_test || ( echo test census_context_test failed ; exit 1 )
+	$(E) "[RUN]     Testing census_resource_test"
+	$(Q) $(BINDIR)/$(CONFIG)/census_resource_test || ( echo test census_resource_test failed ; exit 1 )
+	$(E) "[RUN]     Testing census_trace_context_test"
+	$(Q) $(BINDIR)/$(CONFIG)/census_trace_context_test || ( echo test census_trace_context_test failed ; exit 1 )
 	$(E) "[RUN]     Testing channel_create_test"
 	$(Q) $(BINDIR)/$(CONFIG)/channel_create_test || ( echo test channel_create_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_hpack_encoder_test"
@@ -1546,10 +1572,14 @@
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_stream_map_test || ( echo test chttp2_stream_map_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_varint_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_varint_test || ( echo test chttp2_varint_test failed ; exit 1 )
+	$(E) "[RUN]     Testing combiner_test"
+	$(Q) $(BINDIR)/$(CONFIG)/combiner_test || ( echo test combiner_test failed ; exit 1 )
 	$(E) "[RUN]     Testing compression_test"
 	$(Q) $(BINDIR)/$(CONFIG)/compression_test || ( echo test compression_test failed ; exit 1 )
 	$(E) "[RUN]     Testing concurrent_connectivity_test"
 	$(Q) $(BINDIR)/$(CONFIG)/concurrent_connectivity_test || ( echo test concurrent_connectivity_test failed ; exit 1 )
+	$(E) "[RUN]     Testing connection_refused_test"
+	$(Q) $(BINDIR)/$(CONFIG)/connection_refused_test || ( echo test connection_refused_test failed ; exit 1 )
 	$(E) "[RUN]     Testing dns_resolver_connectivity_test"
 	$(Q) $(BINDIR)/$(CONFIG)/dns_resolver_connectivity_test || ( echo test dns_resolver_connectivity_test failed ; exit 1 )
 	$(E) "[RUN]     Testing dns_resolver_test"
@@ -1586,6 +1616,10 @@
 	$(Q) $(BINDIR)/$(CONFIG)/gpr_host_port_test || ( echo test gpr_host_port_test failed ; exit 1 )
 	$(E) "[RUN]     Testing gpr_log_test"
 	$(Q) $(BINDIR)/$(CONFIG)/gpr_log_test || ( echo test gpr_log_test failed ; exit 1 )
+	$(E) "[RUN]     Testing gpr_mpscq_test"
+	$(Q) $(BINDIR)/$(CONFIG)/gpr_mpscq_test || ( echo test gpr_mpscq_test failed ; exit 1 )
+	$(E) "[RUN]     Testing gpr_percent_encoding_test"
+	$(Q) $(BINDIR)/$(CONFIG)/gpr_percent_encoding_test || ( echo test gpr_percent_encoding_test failed ; exit 1 )
 	$(E) "[RUN]     Testing gpr_slice_buffer_test"
 	$(Q) $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test || ( echo test gpr_slice_buffer_test failed ; exit 1 )
 	$(E) "[RUN]     Testing gpr_slice_test"
@@ -1726,8 +1760,6 @@
 	$(Q) $(BINDIR)/$(CONFIG)/unknown_frame_bad_client_test || ( echo test unknown_frame_bad_client_test failed ; exit 1 )
 	$(E) "[RUN]     Testing window_overflow_bad_client_test"
 	$(Q) $(BINDIR)/$(CONFIG)/window_overflow_bad_client_test || ( echo test window_overflow_bad_client_test failed ; exit 1 )
-	$(E) "[RUN]     Testing bad_ssl_alpn_test"
-	$(Q) $(BINDIR)/$(CONFIG)/bad_ssl_alpn_test || ( echo test bad_ssl_alpn_test failed ; exit 1 )
 	$(E) "[RUN]     Testing bad_ssl_cert_test"
 	$(Q) $(BINDIR)/$(CONFIG)/bad_ssl_cert_test || ( echo test bad_ssl_cert_test failed ; exit 1 )
 
@@ -1768,12 +1800,18 @@
 	$(Q) $(BINDIR)/$(CONFIG)/cxx_time_test || ( echo test cxx_time_test failed ; exit 1 )
 	$(E) "[RUN]     Testing end2end_test"
 	$(Q) $(BINDIR)/$(CONFIG)/end2end_test || ( echo test end2end_test failed ; exit 1 )
+	$(E) "[RUN]     Testing filter_end2end_test"
+	$(Q) $(BINDIR)/$(CONFIG)/filter_end2end_test || ( echo test filter_end2end_test failed ; exit 1 )
 	$(E) "[RUN]     Testing generic_end2end_test"
 	$(Q) $(BINDIR)/$(CONFIG)/generic_end2end_test || ( echo test generic_end2end_test failed ; exit 1 )
 	$(E) "[RUN]     Testing golden_file_test"
 	$(Q) $(BINDIR)/$(CONFIG)/golden_file_test || ( echo test golden_file_test failed ; exit 1 )
+	$(E) "[RUN]     Testing grpc_tool_test"
+	$(Q) $(BINDIR)/$(CONFIG)/grpc_tool_test || ( echo test grpc_tool_test failed ; exit 1 )
 	$(E) "[RUN]     Testing grpclb_api_test"
 	$(Q) $(BINDIR)/$(CONFIG)/grpclb_api_test || ( echo test grpclb_api_test failed ; exit 1 )
+	$(E) "[RUN]     Testing grpclb_test"
+	$(Q) $(BINDIR)/$(CONFIG)/grpclb_test || ( echo test grpclb_test failed ; exit 1 )
 	$(E) "[RUN]     Testing hybrid_end2end_test"
 	$(Q) $(BINDIR)/$(CONFIG)/hybrid_end2end_test || ( echo test hybrid_end2end_test failed ; exit 1 )
 	$(E) "[RUN]     Testing interop_test"
@@ -1813,7 +1851,7 @@
 tools: tools_c tools_cxx
 
 
-tools_c: privatelibs_c $(BINDIR)/$(CONFIG)/gen_hpack_tables $(BINDIR)/$(CONFIG)/gen_legal_metadata_characters $(BINDIR)/$(CONFIG)/grpc_create_jwt $(BINDIR)/$(CONFIG)/grpc_print_google_default_creds_token $(BINDIR)/$(CONFIG)/grpc_verify_jwt
+tools_c: privatelibs_c $(BINDIR)/$(CONFIG)/gen_hpack_tables $(BINDIR)/$(CONFIG)/gen_legal_metadata_characters $(BINDIR)/$(CONFIG)/gen_percent_encoding_tables $(BINDIR)/$(CONFIG)/grpc_create_jwt $(BINDIR)/$(CONFIG)/grpc_print_google_default_creds_token $(BINDIR)/$(CONFIG)/grpc_verify_jwt
 
 tools_cxx: privatelibs_cxx
 
@@ -2149,7 +2187,7 @@
 	$(Q) mkdir -p `dirname $@`
 	$(Q) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -MMD -MF $(addsuffix .dep, $(basename $@)) -c -o $@ $<
 
-install: install_c install_cxx install-plugins install-certs verify-install
+install: install_c install_cxx install-plugins install-certs
 
 install_c: install-headers_c install-static_c install-shared_c
 
@@ -2310,6 +2348,8 @@
 	$(Q) $(INSTALL) -d $(prefix)/bin
 	$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin $(prefix)/bin/grpc_objective_c_plugin
 	$(Q) $(INSTALL) -d $(prefix)/bin
+	$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_php_plugin $(prefix)/bin/grpc_php_plugin
+	$(Q) $(INSTALL) -d $(prefix)/bin
 	$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_python_plugin $(prefix)/bin/grpc_python_plugin
 	$(Q) $(INSTALL) -d $(prefix)/bin
 	$(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_ruby_plugin $(prefix)/bin/grpc_ruby_plugin
@@ -2332,28 +2372,6 @@
 	$(Q) $(INSTALL) -d $(prefix)/share/grpc
 	$(Q) $(INSTALL) etc/roots.pem $(prefix)/share/grpc/roots.pem
 
-verify-install:
-ifeq ($(INSTALL_OK),true)
-	@echo "Your system looks ready to go."
-	@echo
-else
-	@echo "Warning: it looks like protoc 3.0.0+ isn't installed on your system,"
-	@echo "which means that you won't be able to compile .proto files for use"
-	@echo "with gRPC."
-	@echo
-	@echo "If you are just using pre-compiled protocol buffers, or you otherwise"
-	@echo "have no need to compile .proto files, you can ignore this."
-	@echo
-	@echo "If you do need protobuf for some reason, you can download and install"
-	@echo "it from:"
-	@echo
-	@echo "   https://github.com/google/protobuf/releases"
-	@echo
-	@echo "Once you've done so, you can re-run this check by doing:"
-	@echo
-	@echo "   make verify-install"
-endif
-
 clean:
 	$(E) "[CLEAN]   Cleaning build directories."
 	$(Q) $(RM) -rf $(OBJDIR) $(LIBDIR) $(BINDIR) $(GENDIR) cache.mk
@@ -2383,7 +2401,9 @@
     src/core/lib/support/log_linux.c \
     src/core/lib/support/log_posix.c \
     src/core/lib/support/log_windows.c \
+    src/core/lib/support/mpscq.c \
     src/core/lib/support/murmur_hash.c \
+    src/core/lib/support/percent_encoding.c \
     src/core/lib/support/slice.c \
     src/core/lib/support/slice_buffer.c \
     src/core/lib/support/stack_lockfree.c \
@@ -2438,20 +2458,17 @@
     include/grpc/support/tls_msvc.h \
     include/grpc/support/tls_pthread.h \
     include/grpc/support/useful.h \
-    include/grpc/impl/codegen/alloc.h \
     include/grpc/impl/codegen/atm.h \
     include/grpc/impl/codegen/atm_gcc_atomic.h \
     include/grpc/impl/codegen/atm_gcc_sync.h \
     include/grpc/impl/codegen/atm_windows.h \
-    include/grpc/impl/codegen/log.h \
+    include/grpc/impl/codegen/gpr_types.h \
     include/grpc/impl/codegen/port_platform.h \
     include/grpc/impl/codegen/slice.h \
-    include/grpc/impl/codegen/slice_buffer.h \
     include/grpc/impl/codegen/sync.h \
     include/grpc/impl/codegen/sync_generic.h \
     include/grpc/impl/codegen/sync_posix.h \
     include/grpc/impl/codegen/sync_windows.h \
-    include/grpc/impl/codegen/time.h \
 
 LIBGPR_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGPR_SRC))))
 
@@ -2522,9 +2539,11 @@
     src/core/lib/channel/channel_stack_builder.c \
     src/core/lib/channel/compress_filter.c \
     src/core/lib/channel/connected_channel.c \
+    src/core/lib/channel/deadline_filter.c \
     src/core/lib/channel/handshaker.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
+    src/core/lib/channel/message_size_filter.c \
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/debug/trace.c \
@@ -2532,6 +2551,7 @@
     src/core/lib/http/httpcli.c \
     src/core/lib/http/parser.c \
     src/core/lib/iomgr/closure.c \
+    src/core/lib/iomgr/combiner.c \
     src/core/lib/iomgr/endpoint.c \
     src/core/lib/iomgr/endpoint_pair_posix.c \
     src/core/lib/iomgr/endpoint_pair_windows.c \
@@ -2603,6 +2623,7 @@
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
     src/core/lib/transport/static_metadata.c \
+    src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
     src/core/lib/transport/transport_op_string.c \
     src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c \
@@ -2625,7 +2646,6 @@
     src/core/ext/transport/chttp2/transport/status_conversion.c \
     src/core/ext/transport/chttp2/transport/stream_lists.c \
     src/core/ext/transport/chttp2/transport/stream_map.c \
-    src/core/ext/transport/chttp2/transport/timeout_encoding.c \
     src/core/ext/transport/chttp2/transport/varint.c \
     src/core/ext/transport/chttp2/transport/writing.c \
     src/core/ext/transport/chttp2/alpn/alpn.c \
@@ -2661,10 +2681,10 @@
     src/core/ext/client_config/channel_connectivity.c \
     src/core/ext/client_config/client_channel.c \
     src/core/ext/client_config/client_channel_factory.c \
-    src/core/ext/client_config/client_config.c \
     src/core/ext/client_config/client_config_plugin.c \
     src/core/ext/client_config/connector.c \
     src/core/ext/client_config/default_initial_connect_string.c \
+    src/core/ext/client_config/http_connect_handshaker.c \
     src/core/ext/client_config/initial_connect_string.c \
     src/core/ext/client_config/lb_policy.c \
     src/core/ext/client_config/lb_policy_factory.c \
@@ -2673,14 +2693,15 @@
     src/core/ext/client_config/resolver.c \
     src/core/ext/client_config/resolver_factory.c \
     src/core/ext/client_config/resolver_registry.c \
+    src/core/ext/client_config/resolver_result.c \
     src/core/ext/client_config/subchannel.c \
-    src/core/ext/client_config/subchannel_call_holder.c \
     src/core/ext/client_config/subchannel_index.c \
     src/core/ext/client_config/uri_parser.c \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \
+    src/core/ext/lb_policy/grpclb/grpclb.c \
     src/core/ext/lb_policy/grpclb/load_balancer_api.c \
     src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
     third_party/nanopb/pb_common.c \
@@ -2692,8 +2713,10 @@
     src/core/ext/resolver/sockaddr/sockaddr_resolver.c \
     src/core/ext/load_reporting/load_reporting.c \
     src/core/ext/load_reporting/load_reporting_filter.c \
+    src/core/ext/census/base_resources.c \
     src/core/ext/census/context.c \
     src/core/ext/census/gen/census.pb.c \
+    src/core/ext/census/gen/trace_context.pb.c \
     src/core/ext/census/grpc_context.c \
     src/core/ext/census/grpc_filter.c \
     src/core/ext/census/grpc_plugin.c \
@@ -2701,6 +2724,8 @@
     src/core/ext/census/mlog.c \
     src/core/ext/census/operation.c \
     src/core/ext/census/placeholders.c \
+    src/core/ext/census/resource.c \
+    src/core/ext/census/trace_context.c \
     src/core/ext/census/tracing.c \
     src/core/plugin_registry/grpc_plugin_registry.c \
 
@@ -2710,30 +2735,26 @@
     include/grpc/compression.h \
     include/grpc/grpc.h \
     include/grpc/grpc_posix.h \
+    include/grpc/grpc_security_constants.h \
     include/grpc/status.h \
-    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
-    include/grpc/impl/codegen/alloc.h \
     include/grpc/impl/codegen/atm.h \
     include/grpc/impl/codegen/atm_gcc_atomic.h \
     include/grpc/impl/codegen/atm_gcc_sync.h \
     include/grpc/impl/codegen/atm_windows.h \
-    include/grpc/impl/codegen/log.h \
+    include/grpc/impl/codegen/gpr_types.h \
     include/grpc/impl/codegen/port_platform.h \
     include/grpc/impl/codegen/slice.h \
-    include/grpc/impl/codegen/slice_buffer.h \
     include/grpc/impl/codegen/sync.h \
     include/grpc/impl/codegen/sync_generic.h \
     include/grpc/impl/codegen/sync_posix.h \
     include/grpc/impl/codegen/sync_windows.h \
-    include/grpc/impl/codegen/time.h \
     include/grpc/grpc_security.h \
-    include/grpc/grpc_security_constants.h \
     include/grpc/census.h \
 
 LIBGRPC_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_SRC))))
@@ -2795,9 +2816,11 @@
     src/core/lib/channel/channel_stack_builder.c \
     src/core/lib/channel/compress_filter.c \
     src/core/lib/channel/connected_channel.c \
+    src/core/lib/channel/deadline_filter.c \
     src/core/lib/channel/handshaker.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
+    src/core/lib/channel/message_size_filter.c \
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/debug/trace.c \
@@ -2805,6 +2828,7 @@
     src/core/lib/http/httpcli.c \
     src/core/lib/http/parser.c \
     src/core/lib/iomgr/closure.c \
+    src/core/lib/iomgr/combiner.c \
     src/core/lib/iomgr/endpoint.c \
     src/core/lib/iomgr/endpoint_pair_posix.c \
     src/core/lib/iomgr/endpoint_pair_windows.c \
@@ -2876,6 +2900,7 @@
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
     src/core/lib/transport/static_metadata.c \
+    src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
     src/core/lib/transport/transport_op_string.c \
     src/core/ext/transport/cronet/client/secure/cronet_channel_create.c \
@@ -2901,17 +2926,16 @@
     src/core/ext/transport/chttp2/transport/status_conversion.c \
     src/core/ext/transport/chttp2/transport/stream_lists.c \
     src/core/ext/transport/chttp2/transport/stream_map.c \
-    src/core/ext/transport/chttp2/transport/timeout_encoding.c \
     src/core/ext/transport/chttp2/transport/varint.c \
     src/core/ext/transport/chttp2/transport/writing.c \
     src/core/ext/transport/chttp2/alpn/alpn.c \
     src/core/ext/client_config/channel_connectivity.c \
     src/core/ext/client_config/client_channel.c \
     src/core/ext/client_config/client_channel_factory.c \
-    src/core/ext/client_config/client_config.c \
     src/core/ext/client_config/client_config_plugin.c \
     src/core/ext/client_config/connector.c \
     src/core/ext/client_config/default_initial_connect_string.c \
+    src/core/ext/client_config/http_connect_handshaker.c \
     src/core/ext/client_config/initial_connect_string.c \
     src/core/ext/client_config/lb_policy.c \
     src/core/ext/client_config/lb_policy_factory.c \
@@ -2920,8 +2944,8 @@
     src/core/ext/client_config/resolver.c \
     src/core/ext/client_config/resolver_factory.c \
     src/core/ext/client_config/resolver_registry.c \
+    src/core/ext/client_config/resolver_result.c \
     src/core/ext/client_config/subchannel.c \
-    src/core/ext/client_config/subchannel_call_holder.c \
     src/core/ext/client_config/subchannel_index.c \
     src/core/ext/client_config/uri_parser.c \
     src/core/lib/http/httpcli_security_connector.c \
@@ -2960,31 +2984,27 @@
     include/grpc/compression.h \
     include/grpc/grpc.h \
     include/grpc/grpc_posix.h \
+    include/grpc/grpc_security_constants.h \
     include/grpc/status.h \
-    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
-    include/grpc/impl/codegen/alloc.h \
     include/grpc/impl/codegen/atm.h \
     include/grpc/impl/codegen/atm_gcc_atomic.h \
     include/grpc/impl/codegen/atm_gcc_sync.h \
     include/grpc/impl/codegen/atm_windows.h \
-    include/grpc/impl/codegen/log.h \
+    include/grpc/impl/codegen/gpr_types.h \
     include/grpc/impl/codegen/port_platform.h \
     include/grpc/impl/codegen/slice.h \
-    include/grpc/impl/codegen/slice_buffer.h \
     include/grpc/impl/codegen/sync.h \
     include/grpc/impl/codegen/sync_generic.h \
     include/grpc/impl/codegen/sync_posix.h \
     include/grpc/impl/codegen/sync_windows.h \
-    include/grpc/impl/codegen/time.h \
     include/grpc/grpc_cronet.h \
     include/grpc/grpc_security.h \
-    include/grpc/grpc_security_constants.h \
 
 LIBGRPC_CRONET_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_CRONET_SRC))))
 
@@ -3045,6 +3065,8 @@
     test/core/end2end/data/test_root_cert.c \
     test/core/security/oauth2_utils.c \
     test/core/end2end/cq_verifier.c \
+    test/core/end2end/fake_resolver.c \
+    test/core/end2end/fixtures/http_proxy.c \
     test/core/end2end/fixtures/proxy.c \
     test/core/iomgr/endpoint_tests.c \
     test/core/util/grpc_profiler.c \
@@ -3056,90 +3078,16 @@
     test/core/util/port_server_client.c \
     test/core/util/port_windows.c \
     test/core/util/slice_splitter.c \
-
-PUBLIC_HEADERS_C += \
-
-LIBGRPC_TEST_UTIL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_TEST_UTIL_SRC))))
-
-
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure libraries if you don't have OpenSSL.
-
-$(LIBDIR)/$(CONFIG)/libgrpc_test_util.a: openssl_dep_error
-
-
-else
-
-
-$(LIBDIR)/$(CONFIG)/libgrpc_test_util.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_TEST_UTIL_OBJS) 
-	$(E) "[AR]      Creating $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a
-	$(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBGRPC_TEST_UTIL_OBJS) 
-ifeq ($(SYSTEM),Darwin)
-	$(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a
-endif
-
-
-
-
-endif
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(LIBGRPC_TEST_UTIL_OBJS:.o=.dep)
-endif
-endif
-
-
-LIBGRPC_TEST_UTIL_UNSECURE_SRC = \
-    test/core/end2end/cq_verifier.c \
-    test/core/end2end/fixtures/proxy.c \
-    test/core/iomgr/endpoint_tests.c \
-    test/core/util/grpc_profiler.c \
-    test/core/util/memory_counters.c \
-    test/core/util/mock_endpoint.c \
-    test/core/util/parse_hexstring.c \
-    test/core/util/passthru_endpoint.c \
-    test/core/util/port_posix.c \
-    test/core/util/port_server_client.c \
-    test/core/util/port_windows.c \
-    test/core/util/slice_splitter.c \
-
-PUBLIC_HEADERS_C += \
-
-LIBGRPC_TEST_UTIL_UNSECURE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_TEST_UTIL_UNSECURE_SRC))))
-
-
-$(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a: $(ZLIB_DEP)  $(LIBGRPC_TEST_UTIL_UNSECURE_OBJS) 
-	$(E) "[AR]      Creating $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a
-	$(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBGRPC_TEST_UTIL_UNSECURE_OBJS) 
-ifeq ($(SYSTEM),Darwin)
-	$(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a
-endif
-
-
-
-
-ifneq ($(NO_DEPS),true)
--include $(LIBGRPC_TEST_UTIL_UNSECURE_OBJS:.o=.dep)
-endif
-
-
-LIBGRPC_UNSECURE_SRC = \
-    src/core/lib/surface/init.c \
-    src/core/lib/surface/init_unsecure.c \
     src/core/lib/channel/channel_args.c \
     src/core/lib/channel/channel_stack.c \
     src/core/lib/channel/channel_stack_builder.c \
     src/core/lib/channel/compress_filter.c \
     src/core/lib/channel/connected_channel.c \
+    src/core/lib/channel/deadline_filter.c \
     src/core/lib/channel/handshaker.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
+    src/core/lib/channel/message_size_filter.c \
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/debug/trace.c \
@@ -3147,6 +3095,7 @@
     src/core/lib/http/httpcli.c \
     src/core/lib/http/parser.c \
     src/core/lib/iomgr/closure.c \
+    src/core/lib/iomgr/combiner.c \
     src/core/lib/iomgr/endpoint.c \
     src/core/lib/iomgr/endpoint_pair_posix.c \
     src/core/lib/iomgr/endpoint_pair_windows.c \
@@ -3218,6 +3167,201 @@
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
     src/core/lib/transport/static_metadata.c \
+    src/core/lib/transport/timeout_encoding.c \
+    src/core/lib/transport/transport.c \
+    src/core/lib/transport/transport_op_string.c \
+
+PUBLIC_HEADERS_C += \
+    include/grpc/byte_buffer.h \
+    include/grpc/byte_buffer_reader.h \
+    include/grpc/compression.h \
+    include/grpc/grpc.h \
+    include/grpc/grpc_posix.h \
+    include/grpc/grpc_security_constants.h \
+    include/grpc/status.h \
+    include/grpc/impl/codegen/byte_buffer_reader.h \
+    include/grpc/impl/codegen/compression_types.h \
+    include/grpc/impl/codegen/connectivity_state.h \
+    include/grpc/impl/codegen/grpc_types.h \
+    include/grpc/impl/codegen/propagation_bits.h \
+    include/grpc/impl/codegen/status.h \
+    include/grpc/impl/codegen/atm.h \
+    include/grpc/impl/codegen/atm_gcc_atomic.h \
+    include/grpc/impl/codegen/atm_gcc_sync.h \
+    include/grpc/impl/codegen/atm_windows.h \
+    include/grpc/impl/codegen/gpr_types.h \
+    include/grpc/impl/codegen/port_platform.h \
+    include/grpc/impl/codegen/slice.h \
+    include/grpc/impl/codegen/sync.h \
+    include/grpc/impl/codegen/sync_generic.h \
+    include/grpc/impl/codegen/sync_posix.h \
+    include/grpc/impl/codegen/sync_windows.h \
+
+LIBGRPC_TEST_UTIL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_TEST_UTIL_SRC))))
+
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure libraries if you don't have OpenSSL.
+
+$(LIBDIR)/$(CONFIG)/libgrpc_test_util.a: openssl_dep_error
+
+
+else
+
+
+$(LIBDIR)/$(CONFIG)/libgrpc_test_util.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_TEST_UTIL_OBJS) 
+	$(E) "[AR]      Creating $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a
+	$(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBGRPC_TEST_UTIL_OBJS) 
+ifeq ($(SYSTEM),Darwin)
+	$(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a
+endif
+
+
+
+
+endif
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(LIBGRPC_TEST_UTIL_OBJS:.o=.dep)
+endif
+endif
+
+
+LIBGRPC_TEST_UTIL_UNSECURE_SRC = \
+    test/core/end2end/cq_verifier.c \
+    test/core/end2end/fake_resolver.c \
+    test/core/end2end/fixtures/http_proxy.c \
+    test/core/end2end/fixtures/proxy.c \
+    test/core/iomgr/endpoint_tests.c \
+    test/core/util/grpc_profiler.c \
+    test/core/util/memory_counters.c \
+    test/core/util/mock_endpoint.c \
+    test/core/util/parse_hexstring.c \
+    test/core/util/passthru_endpoint.c \
+    test/core/util/port_posix.c \
+    test/core/util/port_server_client.c \
+    test/core/util/port_windows.c \
+    test/core/util/slice_splitter.c \
+
+PUBLIC_HEADERS_C += \
+
+LIBGRPC_TEST_UTIL_UNSECURE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_TEST_UTIL_UNSECURE_SRC))))
+
+
+$(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a: $(ZLIB_DEP)  $(LIBGRPC_TEST_UTIL_UNSECURE_OBJS) 
+	$(E) "[AR]      Creating $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a
+	$(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBGRPC_TEST_UTIL_UNSECURE_OBJS) 
+ifeq ($(SYSTEM),Darwin)
+	$(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a
+endif
+
+
+
+
+ifneq ($(NO_DEPS),true)
+-include $(LIBGRPC_TEST_UTIL_UNSECURE_OBJS:.o=.dep)
+endif
+
+
+LIBGRPC_UNSECURE_SRC = \
+    src/core/lib/surface/init.c \
+    src/core/lib/surface/init_unsecure.c \
+    src/core/lib/channel/channel_args.c \
+    src/core/lib/channel/channel_stack.c \
+    src/core/lib/channel/channel_stack_builder.c \
+    src/core/lib/channel/compress_filter.c \
+    src/core/lib/channel/connected_channel.c \
+    src/core/lib/channel/deadline_filter.c \
+    src/core/lib/channel/handshaker.c \
+    src/core/lib/channel/http_client_filter.c \
+    src/core/lib/channel/http_server_filter.c \
+    src/core/lib/channel/message_size_filter.c \
+    src/core/lib/compression/compression.c \
+    src/core/lib/compression/message_compress.c \
+    src/core/lib/debug/trace.c \
+    src/core/lib/http/format_request.c \
+    src/core/lib/http/httpcli.c \
+    src/core/lib/http/parser.c \
+    src/core/lib/iomgr/closure.c \
+    src/core/lib/iomgr/combiner.c \
+    src/core/lib/iomgr/endpoint.c \
+    src/core/lib/iomgr/endpoint_pair_posix.c \
+    src/core/lib/iomgr/endpoint_pair_windows.c \
+    src/core/lib/iomgr/error.c \
+    src/core/lib/iomgr/ev_epoll_linux.c \
+    src/core/lib/iomgr/ev_poll_and_epoll_posix.c \
+    src/core/lib/iomgr/ev_poll_posix.c \
+    src/core/lib/iomgr/ev_posix.c \
+    src/core/lib/iomgr/exec_ctx.c \
+    src/core/lib/iomgr/executor.c \
+    src/core/lib/iomgr/iocp_windows.c \
+    src/core/lib/iomgr/iomgr.c \
+    src/core/lib/iomgr/iomgr_posix.c \
+    src/core/lib/iomgr/iomgr_windows.c \
+    src/core/lib/iomgr/load_file.c \
+    src/core/lib/iomgr/network_status_tracker.c \
+    src/core/lib/iomgr/polling_entity.c \
+    src/core/lib/iomgr/pollset_set_windows.c \
+    src/core/lib/iomgr/pollset_windows.c \
+    src/core/lib/iomgr/resolve_address_posix.c \
+    src/core/lib/iomgr/resolve_address_windows.c \
+    src/core/lib/iomgr/sockaddr_utils.c \
+    src/core/lib/iomgr/socket_utils_common_posix.c \
+    src/core/lib/iomgr/socket_utils_linux.c \
+    src/core/lib/iomgr/socket_utils_posix.c \
+    src/core/lib/iomgr/socket_windows.c \
+    src/core/lib/iomgr/tcp_client_posix.c \
+    src/core/lib/iomgr/tcp_client_windows.c \
+    src/core/lib/iomgr/tcp_posix.c \
+    src/core/lib/iomgr/tcp_server_posix.c \
+    src/core/lib/iomgr/tcp_server_windows.c \
+    src/core/lib/iomgr/tcp_windows.c \
+    src/core/lib/iomgr/time_averaged_stats.c \
+    src/core/lib/iomgr/timer.c \
+    src/core/lib/iomgr/timer_heap.c \
+    src/core/lib/iomgr/udp_server.c \
+    src/core/lib/iomgr/unix_sockets_posix.c \
+    src/core/lib/iomgr/unix_sockets_posix_noop.c \
+    src/core/lib/iomgr/wakeup_fd_eventfd.c \
+    src/core/lib/iomgr/wakeup_fd_nospecial.c \
+    src/core/lib/iomgr/wakeup_fd_pipe.c \
+    src/core/lib/iomgr/wakeup_fd_posix.c \
+    src/core/lib/iomgr/workqueue_posix.c \
+    src/core/lib/iomgr/workqueue_windows.c \
+    src/core/lib/json/json.c \
+    src/core/lib/json/json_reader.c \
+    src/core/lib/json/json_string.c \
+    src/core/lib/json/json_writer.c \
+    src/core/lib/surface/alarm.c \
+    src/core/lib/surface/api_trace.c \
+    src/core/lib/surface/byte_buffer.c \
+    src/core/lib/surface/byte_buffer_reader.c \
+    src/core/lib/surface/call.c \
+    src/core/lib/surface/call_details.c \
+    src/core/lib/surface/call_log_batch.c \
+    src/core/lib/surface/channel.c \
+    src/core/lib/surface/channel_init.c \
+    src/core/lib/surface/channel_ping.c \
+    src/core/lib/surface/channel_stack_type.c \
+    src/core/lib/surface/completion_queue.c \
+    src/core/lib/surface/event_string.c \
+    src/core/lib/surface/lame_client.c \
+    src/core/lib/surface/metadata_array.c \
+    src/core/lib/surface/server.c \
+    src/core/lib/surface/validate_metadata.c \
+    src/core/lib/surface/version.c \
+    src/core/lib/transport/byte_stream.c \
+    src/core/lib/transport/connectivity_state.c \
+    src/core/lib/transport/metadata.c \
+    src/core/lib/transport/metadata_batch.c \
+    src/core/lib/transport/static_metadata.c \
+    src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
     src/core/lib/transport/transport_op_string.c \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \
@@ -3241,7 +3385,6 @@
     src/core/ext/transport/chttp2/transport/status_conversion.c \
     src/core/ext/transport/chttp2/transport/stream_lists.c \
     src/core/ext/transport/chttp2/transport/stream_map.c \
-    src/core/ext/transport/chttp2/transport/timeout_encoding.c \
     src/core/ext/transport/chttp2/transport/varint.c \
     src/core/ext/transport/chttp2/transport/writing.c \
     src/core/ext/transport/chttp2/alpn/alpn.c \
@@ -3250,10 +3393,10 @@
     src/core/ext/client_config/channel_connectivity.c \
     src/core/ext/client_config/client_channel.c \
     src/core/ext/client_config/client_channel_factory.c \
-    src/core/ext/client_config/client_config.c \
     src/core/ext/client_config/client_config_plugin.c \
     src/core/ext/client_config/connector.c \
     src/core/ext/client_config/default_initial_connect_string.c \
+    src/core/ext/client_config/http_connect_handshaker.c \
     src/core/ext/client_config/initial_connect_string.c \
     src/core/ext/client_config/lb_policy.c \
     src/core/ext/client_config/lb_policy_factory.c \
@@ -3262,14 +3405,15 @@
     src/core/ext/client_config/resolver.c \
     src/core/ext/client_config/resolver_factory.c \
     src/core/ext/client_config/resolver_registry.c \
+    src/core/ext/client_config/resolver_result.c \
     src/core/ext/client_config/subchannel.c \
-    src/core/ext/client_config/subchannel_call_holder.c \
     src/core/ext/client_config/subchannel_index.c \
     src/core/ext/client_config/uri_parser.c \
     src/core/ext/resolver/dns/native/dns_resolver.c \
     src/core/ext/resolver/sockaddr/sockaddr_resolver.c \
     src/core/ext/load_reporting/load_reporting.c \
     src/core/ext/load_reporting/load_reporting_filter.c \
+    src/core/ext/lb_policy/grpclb/grpclb.c \
     src/core/ext/lb_policy/grpclb/load_balancer_api.c \
     src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
     third_party/nanopb/pb_common.c \
@@ -3277,8 +3421,10 @@
     third_party/nanopb/pb_encode.c \
     src/core/ext/lb_policy/pick_first/pick_first.c \
     src/core/ext/lb_policy/round_robin/round_robin.c \
+    src/core/ext/census/base_resources.c \
     src/core/ext/census/context.c \
     src/core/ext/census/gen/census.pb.c \
+    src/core/ext/census/gen/trace_context.pb.c \
     src/core/ext/census/grpc_context.c \
     src/core/ext/census/grpc_filter.c \
     src/core/ext/census/grpc_plugin.c \
@@ -3286,6 +3432,8 @@
     src/core/ext/census/mlog.c \
     src/core/ext/census/operation.c \
     src/core/ext/census/placeholders.c \
+    src/core/ext/census/resource.c \
+    src/core/ext/census/trace_context.c \
     src/core/ext/census/tracing.c \
     src/core/plugin_registry/grpc_unsecure_plugin_registry.c \
 
@@ -3295,28 +3443,25 @@
     include/grpc/compression.h \
     include/grpc/grpc.h \
     include/grpc/grpc_posix.h \
+    include/grpc/grpc_security_constants.h \
     include/grpc/status.h \
-    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
-    include/grpc/impl/codegen/alloc.h \
     include/grpc/impl/codegen/atm.h \
     include/grpc/impl/codegen/atm_gcc_atomic.h \
     include/grpc/impl/codegen/atm_gcc_sync.h \
     include/grpc/impl/codegen/atm_windows.h \
-    include/grpc/impl/codegen/log.h \
+    include/grpc/impl/codegen/gpr_types.h \
     include/grpc/impl/codegen/port_platform.h \
     include/grpc/impl/codegen/slice.h \
-    include/grpc/impl/codegen/slice_buffer.h \
     include/grpc/impl/codegen/sync.h \
     include/grpc/impl/codegen/sync_generic.h \
     include/grpc/impl/codegen/sync_posix.h \
     include/grpc/impl/codegen/sync_windows.h \
-    include/grpc/impl/codegen/time.h \
     include/grpc/census.h \
 
 LIBGRPC_UNSECURE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_UNSECURE_SRC))))
@@ -3435,38 +3580,39 @@
 
 
 LIBGRPC++_SRC = \
+    src/cpp/client/insecure_credentials.cc \
     src/cpp/client/secure_credentials.cc \
     src/cpp/common/auth_property_iterator.cc \
     src/cpp/common/secure_auth_context.cc \
     src/cpp/common/secure_channel_arguments.cc \
     src/cpp/common/secure_create_auth_context.cc \
+    src/cpp/server/insecure_server_credentials.cc \
     src/cpp/server/secure_server_credentials.cc \
-    src/cpp/client/channel.cc \
+    src/cpp/client/channel_cc.cc \
     src/cpp/client/client_context.cc \
     src/cpp/client/create_channel.cc \
     src/cpp/client/create_channel_internal.cc \
     src/cpp/client/create_channel_posix.cc \
-    src/cpp/client/credentials.cc \
+    src/cpp/client/credentials_cc.cc \
     src/cpp/client/generic_stub.cc \
-    src/cpp/client/insecure_credentials.cc \
     src/cpp/common/channel_arguments.cc \
-    src/cpp/common/completion_queue.cc \
+    src/cpp/common/channel_filter.cc \
+    src/cpp/common/completion_queue_cc.cc \
     src/cpp/common/core_codegen.cc \
     src/cpp/common/rpc_method.cc \
     src/cpp/server/async_generic_service.cc \
     src/cpp/server/create_default_thread_pool.cc \
     src/cpp/server/dynamic_thread_pool.cc \
-    src/cpp/server/insecure_server_credentials.cc \
-    src/cpp/server/server.cc \
     src/cpp/server/server_builder.cc \
+    src/cpp/server/server_cc.cc \
     src/cpp/server/server_context.cc \
     src/cpp/server/server_credentials.cc \
     src/cpp/server/server_posix.cc \
-    src/cpp/util/byte_buffer.cc \
-    src/cpp/util/slice.cc \
+    src/cpp/util/byte_buffer_cc.cc \
+    src/cpp/util/slice_cc.cc \
     src/cpp/util/status.cc \
     src/cpp/util/string_ref.cc \
-    src/cpp/util/time.cc \
+    src/cpp/util/time_cc.cc \
     src/cpp/codegen/codegen_init.cc \
 
 PUBLIC_HEADERS_CXX += \
@@ -3540,6 +3686,7 @@
     include/grpc++/impl/codegen/service_type.h \
     include/grpc++/impl/codegen/status.h \
     include/grpc++/impl/codegen/status_code_enum.h \
+    include/grpc++/impl/codegen/status_helper.h \
     include/grpc++/impl/codegen/string_ref.h \
     include/grpc++/impl/codegen/stub_options.h \
     include/grpc++/impl/codegen/sync.h \
@@ -3547,27 +3694,23 @@
     include/grpc++/impl/codegen/sync_no_cxx11.h \
     include/grpc++/impl/codegen/sync_stream.h \
     include/grpc++/impl/codegen/time.h \
-    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
-    include/grpc/impl/codegen/alloc.h \
     include/grpc/impl/codegen/atm.h \
     include/grpc/impl/codegen/atm_gcc_atomic.h \
     include/grpc/impl/codegen/atm_gcc_sync.h \
     include/grpc/impl/codegen/atm_windows.h \
-    include/grpc/impl/codegen/log.h \
+    include/grpc/impl/codegen/gpr_types.h \
     include/grpc/impl/codegen/port_platform.h \
     include/grpc/impl/codegen/slice.h \
-    include/grpc/impl/codegen/slice_buffer.h \
     include/grpc/impl/codegen/sync.h \
     include/grpc/impl/codegen/sync_generic.h \
     include/grpc/impl/codegen/sync_posix.h \
     include/grpc/impl/codegen/sync_windows.h \
-    include/grpc/impl/codegen/time.h \
 
 LIBGRPC++_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_SRC))))
 
@@ -3666,6 +3809,7 @@
     include/grpc++/impl/codegen/service_type.h \
     include/grpc++/impl/codegen/status.h \
     include/grpc++/impl/codegen/status_code_enum.h \
+    include/grpc++/impl/codegen/status_helper.h \
     include/grpc++/impl/codegen/string_ref.h \
     include/grpc++/impl/codegen/stub_options.h \
     include/grpc++/impl/codegen/sync.h \
@@ -3673,27 +3817,23 @@
     include/grpc++/impl/codegen/sync_no_cxx11.h \
     include/grpc++/impl/codegen/sync_stream.h \
     include/grpc++/impl/codegen/time.h \
-    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
-    include/grpc/impl/codegen/alloc.h \
     include/grpc/impl/codegen/atm.h \
     include/grpc/impl/codegen/atm_gcc_atomic.h \
     include/grpc/impl/codegen/atm_gcc_sync.h \
     include/grpc/impl/codegen/atm_windows.h \
-    include/grpc/impl/codegen/log.h \
+    include/grpc/impl/codegen/gpr_types.h \
     include/grpc/impl/codegen/port_platform.h \
     include/grpc/impl/codegen/slice.h \
-    include/grpc/impl/codegen/slice_buffer.h \
     include/grpc/impl/codegen/sync.h \
     include/grpc/impl/codegen/sync_generic.h \
     include/grpc/impl/codegen/sync_posix.h \
     include/grpc/impl/codegen/sync_windows.h \
-    include/grpc/impl/codegen/time.h \
     include/grpc++/impl/codegen/config_protobuf.h \
 
 LIBGRPC++_REFLECTION_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_REFLECTION_SRC))))
@@ -3809,7 +3949,7 @@
 
 
 LIBGRPC++_TEST_CONFIG_SRC = \
-    test/cpp/util/test_config.cc \
+    test/cpp/util/test_config_cc.cc \
 
 PUBLIC_HEADERS_CXX += \
 
@@ -3893,6 +4033,7 @@
     include/grpc++/impl/codegen/service_type.h \
     include/grpc++/impl/codegen/status.h \
     include/grpc++/impl/codegen/status_code_enum.h \
+    include/grpc++/impl/codegen/status_helper.h \
     include/grpc++/impl/codegen/string_ref.h \
     include/grpc++/impl/codegen/stub_options.h \
     include/grpc++/impl/codegen/sync.h \
@@ -3900,29 +4041,27 @@
     include/grpc++/impl/codegen/sync_no_cxx11.h \
     include/grpc++/impl/codegen/sync_stream.h \
     include/grpc++/impl/codegen/time.h \
-    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
-    include/grpc/impl/codegen/alloc.h \
     include/grpc/impl/codegen/atm.h \
     include/grpc/impl/codegen/atm_gcc_atomic.h \
     include/grpc/impl/codegen/atm_gcc_sync.h \
     include/grpc/impl/codegen/atm_windows.h \
-    include/grpc/impl/codegen/log.h \
+    include/grpc/impl/codegen/gpr_types.h \
     include/grpc/impl/codegen/port_platform.h \
     include/grpc/impl/codegen/slice.h \
-    include/grpc/impl/codegen/slice_buffer.h \
     include/grpc/impl/codegen/sync.h \
     include/grpc/impl/codegen/sync_generic.h \
     include/grpc/impl/codegen/sync_posix.h \
     include/grpc/impl/codegen/sync_windows.h \
-    include/grpc/impl/codegen/time.h \
     include/grpc++/impl/codegen/proto_utils.h \
     include/grpc++/impl/codegen/config_protobuf.h \
+    include/grpc++/impl/codegen/thrift_serializer.h \
+    include/grpc++/impl/codegen/thrift_utils.h \
 
 LIBGRPC++_TEST_UTIL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_TEST_UTIL_SRC))))
 
@@ -3976,33 +4115,34 @@
 
 
 LIBGRPC++_UNSECURE_SRC = \
+    src/cpp/client/insecure_credentials.cc \
     src/cpp/common/insecure_create_auth_context.cc \
-    src/cpp/client/channel.cc \
+    src/cpp/server/insecure_server_credentials.cc \
+    src/cpp/client/channel_cc.cc \
     src/cpp/client/client_context.cc \
     src/cpp/client/create_channel.cc \
     src/cpp/client/create_channel_internal.cc \
     src/cpp/client/create_channel_posix.cc \
-    src/cpp/client/credentials.cc \
+    src/cpp/client/credentials_cc.cc \
     src/cpp/client/generic_stub.cc \
-    src/cpp/client/insecure_credentials.cc \
     src/cpp/common/channel_arguments.cc \
-    src/cpp/common/completion_queue.cc \
+    src/cpp/common/channel_filter.cc \
+    src/cpp/common/completion_queue_cc.cc \
     src/cpp/common/core_codegen.cc \
     src/cpp/common/rpc_method.cc \
     src/cpp/server/async_generic_service.cc \
     src/cpp/server/create_default_thread_pool.cc \
     src/cpp/server/dynamic_thread_pool.cc \
-    src/cpp/server/insecure_server_credentials.cc \
-    src/cpp/server/server.cc \
     src/cpp/server/server_builder.cc \
+    src/cpp/server/server_cc.cc \
     src/cpp/server/server_context.cc \
     src/cpp/server/server_credentials.cc \
     src/cpp/server/server_posix.cc \
-    src/cpp/util/byte_buffer.cc \
-    src/cpp/util/slice.cc \
+    src/cpp/util/byte_buffer_cc.cc \
+    src/cpp/util/slice_cc.cc \
     src/cpp/util/status.cc \
     src/cpp/util/string_ref.cc \
-    src/cpp/util/time.cc \
+    src/cpp/util/time_cc.cc \
     src/cpp/codegen/codegen_init.cc \
 
 PUBLIC_HEADERS_CXX += \
@@ -4076,6 +4216,7 @@
     include/grpc++/impl/codegen/service_type.h \
     include/grpc++/impl/codegen/status.h \
     include/grpc++/impl/codegen/status_code_enum.h \
+    include/grpc++/impl/codegen/status_helper.h \
     include/grpc++/impl/codegen/string_ref.h \
     include/grpc++/impl/codegen/stub_options.h \
     include/grpc++/impl/codegen/sync.h \
@@ -4083,27 +4224,23 @@
     include/grpc++/impl/codegen/sync_no_cxx11.h \
     include/grpc++/impl/codegen/sync_stream.h \
     include/grpc++/impl/codegen/time.h \
-    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
     include/grpc/impl/codegen/grpc_types.h \
     include/grpc/impl/codegen/propagation_bits.h \
     include/grpc/impl/codegen/status.h \
-    include/grpc/impl/codegen/alloc.h \
     include/grpc/impl/codegen/atm.h \
     include/grpc/impl/codegen/atm_gcc_atomic.h \
     include/grpc/impl/codegen/atm_gcc_sync.h \
     include/grpc/impl/codegen/atm_windows.h \
-    include/grpc/impl/codegen/log.h \
+    include/grpc/impl/codegen/gpr_types.h \
     include/grpc/impl/codegen/port_platform.h \
     include/grpc/impl/codegen/slice.h \
-    include/grpc/impl/codegen/slice_buffer.h \
     include/grpc/impl/codegen/sync.h \
     include/grpc/impl/codegen/sync_generic.h \
     include/grpc/impl/codegen/sync_posix.h \
     include/grpc/impl/codegen/sync_windows.h \
-    include/grpc/impl/codegen/time.h \
 
 LIBGRPC++_UNSECURE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_UNSECURE_SRC))))
 
@@ -4130,18 +4267,18 @@
 
 
 ifeq ($(SYSTEM),MINGW32)
-$(LIBDIR)/$(CONFIG)/grpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT): $(LIBGRPC++_UNSECURE_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/grpc_unsecure.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/grpc.$(SHARED_EXT)
+$(LIBDIR)/$(CONFIG)/grpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT): $(LIBGRPC++_UNSECURE_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/grpc_unsecure.$(SHARED_EXT)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared grpc++_unsecure.def -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++_unsecure$(SHARED_VERSION).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr-imp -lgrpc_unsecure-imp -lgrpc-imp
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared grpc++_unsecure.def -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++_unsecure$(SHARED_VERSION).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr-imp -lgrpc_unsecure-imp
 else
-$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT): $(LIBGRPC++_UNSECURE_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT)
+$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT): $(LIBGRPC++_UNSECURE_OBJS)  $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT)
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
 ifeq ($(SYSTEM),Darwin)
-	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name $(SHARED_PREFIX)grpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc_unsecure -lgrpc
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name $(SHARED_PREFIX)grpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc_unsecure
 else
-	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc++_unsecure.so.1 -o $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc_unsecure -lgrpc
+	$(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc++_unsecure.so.1 -o $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc_unsecure
 	$(Q) ln -sf $(SHARED_PREFIX)grpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION).so.1
 	$(Q) ln -sf $(SHARED_PREFIX)grpc++_unsecure$(SHARED_VERSION).$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure$(SHARED_VERSION).so
 endif
@@ -4156,8 +4293,11 @@
 
 LIBGRPC_CLI_LIBS_SRC = \
     test/cpp/util/cli_call.cc \
+    test/cpp/util/cli_credentials.cc \
+    test/cpp/util/grpc_tool.cc \
     test/cpp/util/proto_file_parser.cc \
     test/cpp/util/proto_reflection_descriptor_database.cc \
+    test/cpp/util/service_describer.cc \
 
 PUBLIC_HEADERS_CXX += \
 
@@ -4210,6 +4350,7 @@
     src/compiler/csharp_generator.cc \
     src/compiler/node_generator.cc \
     src/compiler/objective_c_generator.cc \
+    src/compiler/php_generator.cc \
     src/compiler/python_generator.cc \
     src/compiler/ruby_generator.cc \
 
@@ -4402,7 +4543,7 @@
 endif
 
 
-LIBINTEROP_SERVER_MAIN_SRC = \
+LIBINTEROP_SERVER_LIB_SRC = \
     $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc \
     $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc \
     $(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc \
@@ -4410,6 +4551,56 @@
 
 PUBLIC_HEADERS_CXX += \
 
+LIBINTEROP_SERVER_LIB_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBINTEROP_SERVER_LIB_SRC))))
+
+
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure libraries if you don't have OpenSSL.
+
+$(LIBDIR)/$(CONFIG)/libinterop_server_lib.a: openssl_dep_error
+
+
+else
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay.
+
+$(LIBDIR)/$(CONFIG)/libinterop_server_lib.a: protobuf_dep_error
+
+
+else
+
+$(LIBDIR)/$(CONFIG)/libinterop_server_lib.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBINTEROP_SERVER_LIB_OBJS) 
+	$(E) "[AR]      Creating $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a
+	$(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBINTEROP_SERVER_LIB_OBJS) 
+ifeq ($(SYSTEM),Darwin)
+	$(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a
+endif
+
+
+
+
+endif
+
+endif
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(LIBINTEROP_SERVER_LIB_OBJS:.o=.dep)
+endif
+endif
+$(OBJDIR)/$(CONFIG)/test/cpp/interop/interop_server.o: $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc
+
+
+LIBINTEROP_SERVER_MAIN_SRC = \
+    test/cpp/interop/interop_server_bootstrap.cc \
+
+PUBLIC_HEADERS_CXX += \
+
 LIBINTEROP_SERVER_MAIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBINTEROP_SERVER_MAIN_SRC))))
 
 
@@ -4452,7 +4643,6 @@
 -include $(LIBINTEROP_SERVER_MAIN_OBJS:.o=.dep)
 endif
 endif
-$(OBJDIR)/$(CONFIG)/test/cpp/interop/interop_server.o: $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/test.pb.cc $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc
 
 
 LIBQPS_SRC = \
@@ -6396,6 +6586,7 @@
     test/core/end2end/tests/default_host.c \
     test/core/end2end/tests/disappearing_server.c \
     test/core/end2end/tests/empty_batch.c \
+    test/core/end2end/tests/filter_call_init_fails.c \
     test/core/end2end/tests/filter_causes_close.c \
     test/core/end2end/tests/graceful_server_shutdown.c \
     test/core/end2end/tests/high_initial_seqno.c \
@@ -6403,10 +6594,12 @@
     test/core/end2end/tests/idempotent_request.c \
     test/core/end2end/tests/invoke_large_request.c \
     test/core/end2end/tests/large_metadata.c \
+    test/core/end2end/tests/load_reporting_hook.c \
     test/core/end2end/tests/max_concurrent_streams.c \
     test/core/end2end/tests/max_message_length.c \
     test/core/end2end/tests/negative_deadline.c \
     test/core/end2end/tests/network_status_change.c \
+    test/core/end2end/tests/no_logging.c \
     test/core/end2end/tests/no_op.c \
     test/core/end2end/tests/payload.c \
     test/core/end2end/tests/ping.c \
@@ -6417,6 +6610,7 @@
     test/core/end2end/tests/server_finishes_request.c \
     test/core/end2end/tests/shutdown_finishes_calls.c \
     test/core/end2end/tests/shutdown_finishes_tags.c \
+    test/core/end2end/tests/simple_cacheable_request.c \
     test/core/end2end/tests/simple_delayed_request.c \
     test/core/end2end/tests/simple_metadata.c \
     test/core/end2end/tests/simple_request.c \
@@ -6474,6 +6668,7 @@
     test/core/end2end/tests/default_host.c \
     test/core/end2end/tests/disappearing_server.c \
     test/core/end2end/tests/empty_batch.c \
+    test/core/end2end/tests/filter_call_init_fails.c \
     test/core/end2end/tests/filter_causes_close.c \
     test/core/end2end/tests/graceful_server_shutdown.c \
     test/core/end2end/tests/high_initial_seqno.c \
@@ -6481,10 +6676,12 @@
     test/core/end2end/tests/idempotent_request.c \
     test/core/end2end/tests/invoke_large_request.c \
     test/core/end2end/tests/large_metadata.c \
+    test/core/end2end/tests/load_reporting_hook.c \
     test/core/end2end/tests/max_concurrent_streams.c \
     test/core/end2end/tests/max_message_length.c \
     test/core/end2end/tests/negative_deadline.c \
     test/core/end2end/tests/network_status_change.c \
+    test/core/end2end/tests/no_logging.c \
     test/core/end2end/tests/no_op.c \
     test/core/end2end/tests/payload.c \
     test/core/end2end/tests/ping.c \
@@ -6495,6 +6692,7 @@
     test/core/end2end/tests/server_finishes_request.c \
     test/core/end2end/tests/shutdown_finishes_calls.c \
     test/core/end2end/tests/shutdown_finishes_tags.c \
+    test/core/end2end/tests/simple_cacheable_request.c \
     test/core/end2end/tests/simple_delayed_request.c \
     test/core/end2end/tests/simple_metadata.c \
     test/core/end2end/tests/simple_request.c \
@@ -6815,6 +7013,70 @@
 endif
 
 
+CENSUS_RESOURCE_TEST_SRC = \
+    test/core/census/resource_test.c \
+
+CENSUS_RESOURCE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CENSUS_RESOURCE_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/census_resource_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/census_resource_test: $(CENSUS_RESOURCE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(CENSUS_RESOURCE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/census_resource_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/census/resource_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_census_resource_test: $(CENSUS_RESOURCE_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(CENSUS_RESOURCE_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+CENSUS_TRACE_CONTEXT_TEST_SRC = \
+    test/core/census/trace_context_test.c \
+
+CENSUS_TRACE_CONTEXT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CENSUS_TRACE_CONTEXT_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/census_trace_context_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/census_trace_context_test: $(CENSUS_TRACE_CONTEXT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(CENSUS_TRACE_CONTEXT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/census_trace_context_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/census/trace_context_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_census_trace_context_test: $(CENSUS_TRACE_CONTEXT_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(CENSUS_TRACE_CONTEXT_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 CHANNEL_CREATE_TEST_SRC = \
     test/core/surface/channel_create_test.c \
 
@@ -7007,6 +7269,38 @@
 endif
 
 
+COMBINER_TEST_SRC = \
+    test/core/iomgr/combiner_test.c \
+
+COMBINER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(COMBINER_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/combiner_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/combiner_test: $(COMBINER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(COMBINER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/combiner_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/iomgr/combiner_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_combiner_test: $(COMBINER_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(COMBINER_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 COMPRESSION_TEST_SRC = \
     test/core/compression/compression_test.c \
 
@@ -7071,6 +7365,38 @@
 endif
 
 
+CONNECTION_REFUSED_TEST_SRC = \
+    test/core/end2end/connection_refused_test.c \
+
+CONNECTION_REFUSED_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CONNECTION_REFUSED_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/connection_refused_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/connection_refused_test: $(CONNECTION_REFUSED_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(CONNECTION_REFUSED_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/connection_refused_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/end2end/connection_refused_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_connection_refused_test: $(CONNECTION_REFUSED_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(CONNECTION_REFUSED_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 DNS_RESOLVER_CONNECTIVITY_TEST_SRC = \
     test/core/client_config/resolvers/dns_resolver_connectivity_test.c \
 
@@ -7487,6 +7813,38 @@
 endif
 
 
+GEN_PERCENT_ENCODING_TABLES_SRC = \
+    tools/codegen/core/gen_percent_encoding_tables.c \
+
+GEN_PERCENT_ENCODING_TABLES_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GEN_PERCENT_ENCODING_TABLES_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/gen_percent_encoding_tables: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/gen_percent_encoding_tables: $(GEN_PERCENT_ENCODING_TABLES_OBJS)
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(GEN_PERCENT_ENCODING_TABLES_OBJS) $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gen_percent_encoding_tables
+
+endif
+
+$(OBJDIR)/$(CONFIG)/tools/codegen/core/gen_percent_encoding_tables.o: 
+
+deps_gen_percent_encoding_tables: $(GEN_PERCENT_ENCODING_TABLES_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GEN_PERCENT_ENCODING_TABLES_OBJS:.o=.dep)
+endif
+endif
+
+
 GOAWAY_SERVER_TEST_SRC = \
     test/core/end2end/goaway_server_test.c \
 
@@ -7775,6 +8133,70 @@
 endif
 
 
+GPR_MPSCQ_TEST_SRC = \
+    test/core/support/mpscq_test.c \
+
+GPR_MPSCQ_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_MPSCQ_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/gpr_mpscq_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/gpr_mpscq_test: $(GPR_MPSCQ_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(GPR_MPSCQ_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_mpscq_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/support/mpscq_test.o:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_gpr_mpscq_test: $(GPR_MPSCQ_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GPR_MPSCQ_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+GPR_PERCENT_ENCODING_TEST_SRC = \
+    test/core/support/percent_encoding_test.c \
+
+GPR_PERCENT_ENCODING_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_PERCENT_ENCODING_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/gpr_percent_encoding_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/gpr_percent_encoding_test: $(GPR_PERCENT_ENCODING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(GPR_PERCENT_ENCODING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_percent_encoding_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/support/percent_encoding_test.o:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_gpr_percent_encoding_test: $(GPR_PERCENT_ENCODING_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GPR_PERCENT_ENCODING_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 GPR_SLICE_BUFFER_TEST_SRC = \
     test/core/support/slice_buffer_test.c \
 
@@ -9503,6 +9925,70 @@
 endif
 
 
+PERCENT_DECODE_FUZZER_SRC = \
+    test/core/support/percent_decode_fuzzer.c \
+
+PERCENT_DECODE_FUZZER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(PERCENT_DECODE_FUZZER_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/percent_decode_fuzzer: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/percent_decode_fuzzer: $(PERCENT_DECODE_FUZZER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(PERCENT_DECODE_FUZZER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -lFuzzer -o $(BINDIR)/$(CONFIG)/percent_decode_fuzzer
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/support/percent_decode_fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_percent_decode_fuzzer: $(PERCENT_DECODE_FUZZER_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(PERCENT_DECODE_FUZZER_OBJS:.o=.dep)
+endif
+endif
+
+
+PERCENT_ENCODE_FUZZER_SRC = \
+    test/core/support/percent_encode_fuzzer.c \
+
+PERCENT_ENCODE_FUZZER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(PERCENT_ENCODE_FUZZER_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/percent_encode_fuzzer: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/percent_encode_fuzzer: $(PERCENT_ENCODE_FUZZER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(PERCENT_ENCODE_FUZZER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -lFuzzer -o $(BINDIR)/$(CONFIG)/percent_encode_fuzzer
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/support/percent_encode_fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_percent_encode_fuzzer: $(PERCENT_ENCODE_FUZZER_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(PERCENT_ENCODE_FUZZER_OBJS:.o=.dep)
+endif
+endif
+
+
 RESOLVE_ADDRESS_TEST_SRC = \
     test/core/iomgr/resolve_address_test.c \
 
@@ -9984,7 +10470,7 @@
 
 
 TIMEOUT_ENCODING_TEST_SRC = \
-    test/core/transport/chttp2/timeout_encoding_test.c \
+    test/core/transport/timeout_encoding_test.c \
 
 TIMEOUT_ENCODING_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TIMEOUT_ENCODING_TEST_SRC))))
 ifeq ($(NO_SECURE),true)
@@ -10004,7 +10490,7 @@
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/core/transport/chttp2/timeout_encoding_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/core/transport/timeout_encoding_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
 deps_timeout_encoding_test: $(TIMEOUT_ENCODING_TEST_OBJS:.o=.dep)
 
@@ -10952,6 +11438,49 @@
 endif
 
 
+FILTER_END2END_TEST_SRC = \
+    test/cpp/end2end/filter_end2end_test.cc \
+
+FILTER_END2END_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(FILTER_END2END_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/filter_end2end_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/filter_end2end_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/filter_end2end_test: $(PROTOBUF_DEP) $(FILTER_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(FILTER_END2END_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/filter_end2end_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/cpp/end2end/filter_end2end_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_filter_end2end_test: $(FILTER_END2END_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(FILTER_END2END_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 GENERIC_END2END_TEST_SRC = \
     test/cpp/end2end/generic_end2end_test.cc \
 
@@ -11065,16 +11594,16 @@
 
 else
 
-$(BINDIR)/$(CONFIG)/grpc_cli: $(PROTOBUF_DEP) $(GRPC_CLI_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
+$(BINDIR)/$(CONFIG)/grpc_cli: $(PROTOBUF_DEP) $(GRPC_CLI_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) $(GRPC_CLI_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/grpc_cli
+	$(Q) $(LDXX) $(LDFLAGS) $(GRPC_CLI_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/grpc_cli
 
 endif
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/cpp/util/grpc_cli.o:  $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
+$(OBJDIR)/$(CONFIG)/test/cpp/util/grpc_cli.o:  $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
 
 deps_grpc_cli: $(GRPC_CLI_OBJS:.o=.dep)
 
@@ -11209,6 +11738,37 @@
 endif
 
 
+GRPC_PHP_PLUGIN_SRC = \
+    src/compiler/php_plugin.cc \
+
+GRPC_PHP_PLUGIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_PHP_PLUGIN_SRC))))
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/grpc_php_plugin: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/grpc_php_plugin: $(PROTOBUF_DEP) $(GRPC_PHP_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a
+	$(E) "[HOSTLD]  Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(HOST_LDXX) $(HOST_LDFLAGS) $(GRPC_PHP_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a $(HOST_LDLIBSXX) $(HOST_LDLIBS_PROTOC) $(HOST_LDLIBS) $(HOST_LDLIBS_PROTOC) -o $(BINDIR)/$(CONFIG)/grpc_php_plugin
+
+endif
+
+$(OBJDIR)/$(CONFIG)/src/compiler/php_plugin.o:  $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a
+
+deps_grpc_php_plugin: $(GRPC_PHP_PLUGIN_OBJS:.o=.dep)
+
+ifneq ($(NO_DEPS),true)
+-include $(GRPC_PHP_PLUGIN_OBJS:.o=.dep)
+endif
+
+
 GRPC_PYTHON_PLUGIN_SRC = \
     src/compiler/python_plugin.cc \
 
@@ -11271,6 +11831,60 @@
 endif
 
 
+GRPC_TOOL_TEST_SRC = \
+    $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc \
+    $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc \
+    test/cpp/util/grpc_tool_test.cc \
+    test/cpp/util/string_ref_helper.cc \
+
+GRPC_TOOL_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_TOOL_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/grpc_tool_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/grpc_tool_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/grpc_tool_test: $(PROTOBUF_DEP) $(GRPC_TOOL_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(GRPC_TOOL_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/grpc_tool_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/echo.o:  $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/echo_messages.o:  $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+$(OBJDIR)/$(CONFIG)/test/cpp/util/grpc_tool_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+$(OBJDIR)/$(CONFIG)/test/cpp/util/string_ref_helper.o:  $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_grpc_tool_test: $(GRPC_TOOL_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GRPC_TOOL_TEST_OBJS:.o=.dep)
+endif
+endif
+$(OBJDIR)/$(CONFIG)/test/cpp/util/grpc_tool_test.o: $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/util/string_ref_helper.o: $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc
+
+
 GRPCLB_API_TEST_SRC = \
     $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc \
     test/cpp/grpclb/grpclb_api_test.cc \
@@ -11318,6 +11932,53 @@
 $(OBJDIR)/$(CONFIG)/test/cpp/grpclb/grpclb_api_test.o: $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc
 
 
+GRPCLB_TEST_SRC = \
+    $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc \
+    test/cpp/grpclb/grpclb_test.cc \
+
+GRPCLB_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPCLB_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/grpclb_test: openssl_dep_error
+
+else
+
+
+
+
+ifeq ($(NO_PROTOBUF),true)
+
+# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
+
+$(BINDIR)/$(CONFIG)/grpclb_test: protobuf_dep_error
+
+else
+
+$(BINDIR)/$(CONFIG)/grpclb_test: $(PROTOBUF_DEP) $(GRPCLB_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(GRPCLB_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/grpclb_test
+
+endif
+
+endif
+
+$(OBJDIR)/$(CONFIG)/src/proto/grpc/lb/v1/load_balancer.o:  $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a
+
+$(OBJDIR)/$(CONFIG)/test/cpp/grpclb/grpclb_test.o:  $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a
+
+deps_grpclb_test: $(GRPCLB_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(GRPCLB_TEST_OBJS:.o=.dep)
+endif
+endif
+$(OBJDIR)/$(CONFIG)/test/cpp/grpclb/grpclb_test.o: $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc
+
+
 HYBRID_END2END_TEST_SRC = \
     test/cpp/end2end/hybrid_end2end_test.cc \
 
@@ -11411,10 +12072,10 @@
 
 else
 
-$(BINDIR)/$(CONFIG)/interop_server:  $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
+$(BINDIR)/$(CONFIG)/interop_server:  $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS)  $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/interop_server
+	$(Q) $(LDXX) $(LDFLAGS)  $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_lib.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/interop_server
 
 endif
 
@@ -13640,38 +14301,6 @@
 endif
 
 
-BAD_SSL_ALPN_SERVER_SRC = \
-    test/core/bad_ssl/servers/alpn.c \
-
-BAD_SSL_ALPN_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(BAD_SSL_ALPN_SERVER_SRC))))
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure targets if you don't have OpenSSL.
-
-$(BINDIR)/$(CONFIG)/bad_ssl_alpn_server: openssl_dep_error
-
-else
-
-
-
-$(BINDIR)/$(CONFIG)/bad_ssl_alpn_server: $(BAD_SSL_ALPN_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libbad_ssl_test_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-	$(E) "[LD]      Linking $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(BAD_SSL_ALPN_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libbad_ssl_test_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/bad_ssl_alpn_server
-
-endif
-
-$(OBJDIR)/$(CONFIG)/test/core/bad_ssl/servers/alpn.o:  $(LIBDIR)/$(CONFIG)/libbad_ssl_test_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-
-deps_bad_ssl_alpn_server: $(BAD_SSL_ALPN_SERVER_OBJS:.o=.dep)
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(BAD_SSL_ALPN_SERVER_OBJS:.o=.dep)
-endif
-endif
-
-
 BAD_SSL_CERT_SERVER_SRC = \
     test/core/bad_ssl/servers/cert.c \
 
@@ -13704,38 +14333,6 @@
 endif
 
 
-BAD_SSL_ALPN_TEST_SRC = \
-    test/core/bad_ssl/bad_ssl_test.c \
-
-BAD_SSL_ALPN_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(BAD_SSL_ALPN_TEST_SRC))))
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure targets if you don't have OpenSSL.
-
-$(BINDIR)/$(CONFIG)/bad_ssl_alpn_test: openssl_dep_error
-
-else
-
-
-
-$(BINDIR)/$(CONFIG)/bad_ssl_alpn_test: $(BAD_SSL_ALPN_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-	$(E) "[LD]      Linking $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(BAD_SSL_ALPN_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/bad_ssl_alpn_test
-
-endif
-
-$(OBJDIR)/$(CONFIG)/test/core/bad_ssl/bad_ssl_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-
-deps_bad_ssl_alpn_test: $(BAD_SSL_ALPN_TEST_OBJS:.o=.dep)
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(BAD_SSL_ALPN_TEST_OBJS:.o=.dep)
-endif
-endif
-
-
 BAD_SSL_CERT_TEST_SRC = \
     test/core/bad_ssl/bad_ssl_test.c \
 
@@ -13992,34 +14589,66 @@
 endif
 
 
-H2_LOADREPORTING_TEST_SRC = \
-    test/core/end2end/fixtures/h2_loadreporting.c \
+H2_HTTP_PROXY_TEST_SRC = \
+    test/core/end2end/fixtures/h2_http_proxy.c \
 
-H2_LOADREPORTING_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_LOADREPORTING_TEST_SRC))))
+H2_HTTP_PROXY_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_HTTP_PROXY_TEST_SRC))))
 ifeq ($(NO_SECURE),true)
 
 # You can't build secure targets if you don't have OpenSSL.
 
-$(BINDIR)/$(CONFIG)/h2_loadreporting_test: openssl_dep_error
+$(BINDIR)/$(CONFIG)/h2_http_proxy_test: openssl_dep_error
 
 else
 
 
 
-$(BINDIR)/$(CONFIG)/h2_loadreporting_test: $(H2_LOADREPORTING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(BINDIR)/$(CONFIG)/h2_http_proxy_test: $(H2_HTTP_PROXY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(H2_LOADREPORTING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_loadreporting_test
+	$(Q) $(LD) $(LDFLAGS) $(H2_HTTP_PROXY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_http_proxy_test
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/h2_loadreporting.o:  $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/h2_http_proxy.o:  $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
-deps_h2_loadreporting_test: $(H2_LOADREPORTING_TEST_OBJS:.o=.dep)
+deps_h2_http_proxy_test: $(H2_HTTP_PROXY_TEST_OBJS:.o=.dep)
 
 ifneq ($(NO_SECURE),true)
 ifneq ($(NO_DEPS),true)
--include $(H2_LOADREPORTING_TEST_OBJS:.o=.dep)
+-include $(H2_HTTP_PROXY_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+H2_LOAD_REPORTING_TEST_SRC = \
+    test/core/end2end/fixtures/h2_load_reporting.c \
+
+H2_LOAD_REPORTING_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_LOAD_REPORTING_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/h2_load_reporting_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/h2_load_reporting_test: $(H2_LOAD_REPORTING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(H2_LOAD_REPORTING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_load_reporting_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/h2_load_reporting.o:  $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_h2_load_reporting_test: $(H2_LOAD_REPORTING_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(H2_LOAD_REPORTING_TEST_OBJS:.o=.dep)
 endif
 endif
 
@@ -14432,23 +15061,43 @@
 endif
 
 
-H2_LOADREPORTING_NOSEC_TEST_SRC = \
-    test/core/end2end/fixtures/h2_loadreporting.c \
+H2_HTTP_PROXY_NOSEC_TEST_SRC = \
+    test/core/end2end/fixtures/h2_http_proxy.c \
 
-H2_LOADREPORTING_NOSEC_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_LOADREPORTING_NOSEC_TEST_SRC))))
+H2_HTTP_PROXY_NOSEC_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_HTTP_PROXY_NOSEC_TEST_SRC))))
 
 
-$(BINDIR)/$(CONFIG)/h2_loadreporting_nosec_test: $(H2_LOADREPORTING_NOSEC_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(BINDIR)/$(CONFIG)/h2_http_proxy_nosec_test: $(H2_HTTP_PROXY_NOSEC_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(H2_LOADREPORTING_NOSEC_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_loadreporting_nosec_test
+	$(Q) $(LD) $(LDFLAGS) $(H2_HTTP_PROXY_NOSEC_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_http_proxy_nosec_test
 
-$(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/h2_loadreporting.o:  $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/h2_http_proxy.o:  $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
-deps_h2_loadreporting_nosec_test: $(H2_LOADREPORTING_NOSEC_TEST_OBJS:.o=.dep)
+deps_h2_http_proxy_nosec_test: $(H2_HTTP_PROXY_NOSEC_TEST_OBJS:.o=.dep)
 
 ifneq ($(NO_DEPS),true)
--include $(H2_LOADREPORTING_NOSEC_TEST_OBJS:.o=.dep)
+-include $(H2_HTTP_PROXY_NOSEC_TEST_OBJS:.o=.dep)
+endif
+
+
+H2_LOAD_REPORTING_NOSEC_TEST_SRC = \
+    test/core/end2end/fixtures/h2_load_reporting.c \
+
+H2_LOAD_REPORTING_NOSEC_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_LOAD_REPORTING_NOSEC_TEST_SRC))))
+
+
+$(BINDIR)/$(CONFIG)/h2_load_reporting_nosec_test: $(H2_LOAD_REPORTING_NOSEC_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(H2_LOAD_REPORTING_NOSEC_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_load_reporting_nosec_test
+
+$(OBJDIR)/$(CONFIG)/test/core/end2end/fixtures/h2_load_reporting.o:  $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_h2_load_reporting_nosec_test: $(H2_LOAD_REPORTING_NOSEC_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_DEPS),true)
+-include $(H2_LOAD_REPORTING_NOSEC_TEST_OBJS:.o=.dep)
 endif
 
 
@@ -14832,6 +15481,76 @@
 endif
 
 
+PERCENT_DECODE_FUZZER_ONE_ENTRY_SRC = \
+    test/core/support/percent_decode_fuzzer.c \
+    test/core/util/one_corpus_entry_fuzzer.c \
+
+PERCENT_DECODE_FUZZER_ONE_ENTRY_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(PERCENT_DECODE_FUZZER_ONE_ENTRY_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/percent_decode_fuzzer_one_entry: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/percent_decode_fuzzer_one_entry: $(PERCENT_DECODE_FUZZER_ONE_ENTRY_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(PERCENT_DECODE_FUZZER_ONE_ENTRY_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/percent_decode_fuzzer_one_entry
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/support/percent_decode_fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+$(OBJDIR)/$(CONFIG)/test/core/util/one_corpus_entry_fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_percent_decode_fuzzer_one_entry: $(PERCENT_DECODE_FUZZER_ONE_ENTRY_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(PERCENT_DECODE_FUZZER_ONE_ENTRY_OBJS:.o=.dep)
+endif
+endif
+
+
+PERCENT_ENCODE_FUZZER_ONE_ENTRY_SRC = \
+    test/core/support/percent_encode_fuzzer.c \
+    test/core/util/one_corpus_entry_fuzzer.c \
+
+PERCENT_ENCODE_FUZZER_ONE_ENTRY_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(PERCENT_ENCODE_FUZZER_ONE_ENTRY_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/percent_encode_fuzzer_one_entry: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/percent_encode_fuzzer_one_entry: $(PERCENT_ENCODE_FUZZER_ONE_ENTRY_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(PERCENT_ENCODE_FUZZER_ONE_ENTRY_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/percent_encode_fuzzer_one_entry
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/support/percent_encode_fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+$(OBJDIR)/$(CONFIG)/test/core/util/one_corpus_entry_fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_percent_encode_fuzzer_one_entry: $(PERCENT_ENCODE_FUZZER_ONE_ENTRY_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(PERCENT_ENCODE_FUZZER_ONE_ENTRY_OBJS:.o=.dep)
+endif
+endif
+
+
 SERVER_FUZZER_ONE_ENTRY_SRC = \
     test/core/end2end/fuzzers/server_fuzzer.c \
     test/core/util/one_corpus_entry_fuzzer.c \
@@ -14972,6 +15691,7 @@
 test/cpp/interop/client_helper.cc: $(OPENSSL_DEP)
 test/cpp/interop/interop_client.cc: $(OPENSSL_DEP)
 test/cpp/interop/interop_server.cc: $(OPENSSL_DEP)
+test/cpp/interop/interop_server_bootstrap.cc: $(OPENSSL_DEP)
 test/cpp/interop/server_helper.cc: $(OPENSSL_DEP)
 test/cpp/qps/client_async.cc: $(OPENSSL_DEP)
 test/cpp/qps/client_sync.cc: $(OPENSSL_DEP)
@@ -14986,12 +15706,15 @@
 test/cpp/util/benchmark_config.cc: $(OPENSSL_DEP)
 test/cpp/util/byte_buffer_proto_helper.cc: $(OPENSSL_DEP)
 test/cpp/util/cli_call.cc: $(OPENSSL_DEP)
+test/cpp/util/cli_credentials.cc: $(OPENSSL_DEP)
 test/cpp/util/create_test_channel.cc: $(OPENSSL_DEP)
+test/cpp/util/grpc_tool.cc: $(OPENSSL_DEP)
 test/cpp/util/proto_file_parser.cc: $(OPENSSL_DEP)
 test/cpp/util/proto_reflection_descriptor_database.cc: $(OPENSSL_DEP)
+test/cpp/util/service_describer.cc: $(OPENSSL_DEP)
 test/cpp/util/string_ref_helper.cc: $(OPENSSL_DEP)
 test/cpp/util/subprocess.cc: $(OPENSSL_DEP)
-test/cpp/util/test_config.cc: $(OPENSSL_DEP)
+test/cpp/util/test_config_cc.cc: $(OPENSSL_DEP)
 test/cpp/util/test_credentials_provider.cc: $(OPENSSL_DEP)
 endif
 
diff --git a/PYTHON-MANIFEST.in b/PYTHON-MANIFEST.in
index 96bcdb5..adeb390 100644
--- a/PYTHON-MANIFEST.in
+++ b/PYTHON-MANIFEST.in
@@ -7,7 +7,7 @@
 graft third_party/boringssl
 graft third_party/nanopb
 graft third_party/zlib
-include src/python/grpcio/_unixccompiler_patch.py
+include src/python/grpcio/_spawn_patch.py
 include src/python/grpcio/commands.py
 include src/python/grpcio/grpc_version.py
 include src/python/grpcio/grpc_core_dependencies.py
diff --git a/README.md b/README.md
index 3283517..e9b0454 100644
--- a/README.md
+++ b/README.md
@@ -23,16 +23,16 @@
 
 Libraries in different languages are in different states of development. We are seeking contributions for all of these libraries.
 
-| Language                | Source                              | Status                           |
-|-------------------------|-------------------------------------|----------------------------------|
-| Shared C [core library] | [src/core] (src/core)               | Beta - the surface API is stable |
-| C++                     | [src/cpp] (src/cpp)                 | Beta - the surface API is stable |
-| Ruby                    | [src/ruby] (src/ruby)               | Beta - the surface API is stable |
-| NodeJS                  | [src/node] (src/node)               | Beta - the surface API is stable |
-| Python                  | [src/python] (src/python)           | Beta - the surface API is stable |
-| PHP                     | [src/php] (src/php)                 | Beta - the surface API is stable |
-| C#                      | [src/csharp] (src/csharp)           | Beta - the surface API is stable |
-| Objective-C             | [src/objective-c] (src/objective-c) | Beta - the surface API is stable |
+| Language                | Source                              | Status  |
+|-------------------------|-------------------------------------|---------|
+| Shared C [core library] | [src/core] (src/core)               | 1.0     |
+| C++                     | [src/cpp] (src/cpp)                 | 1.0     |
+| Ruby                    | [src/ruby] (src/ruby)               | 1.0     |
+| NodeJS                  | [src/node] (src/node)               | 1.0     |
+| Python                  | [src/python] (src/python)           | 1.0     |
+| PHP                     | [src/php] (src/php)                 | 1.0     |
+| C#                      | [src/csharp] (src/csharp)           | 1.0     |
+| Objective-C             | [src/objective-c] (src/objective-c) | 1.0     |
 
 <small>
 Java source code is in the [grpc-java] (http://github.com/grpc/grpc-java) repository.
diff --git a/Rakefile b/Rakefile
index 5b6f101..cfd4181 100755
--- a/Rakefile
+++ b/Rakefile
@@ -100,13 +100,15 @@
 task 'gem:native' do
   verbose = ENV['V'] || '0'
 
+  grpc_config = ENV['GRPC_CONFIG'] || 'opt'
+
   if RUBY_PLATFORM =~ /darwin/
     FileUtils.touch 'grpc_c.32.ruby'
     FileUtils.touch 'grpc_c.64.ruby'
-    system "rake cross native gem RUBY_CC_VERSION=2.3.0:2.2.2:2.1.5:2.0.0 V=#{verbose}"
+    system "rake cross native gem RUBY_CC_VERSION=2.3.0:2.2.2:2.1.5:2.0.0 V=#{verbose} GRPC_CONFIG=#{grpc_config}"
   else
     Rake::Task['dlls'].execute
-    docker_for_windows "bundle && rake cross native gem RUBY_CC_VERSION=2.3.0:2.2.2:2.1.5:2.0.0 V=#{verbose}"
+    docker_for_windows "bundle && rake cross native gem RUBY_CC_VERSION=2.3.0:2.2.2:2.1.5:2.0.0 V=#{verbose} GRPC_CONFIG=#{grpc_config}"
   end
 end
 
diff --git a/binding.gyp b/binding.gyp
index 1dd7ee9..58edda2 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -515,7 +515,9 @@
         'src/core/lib/support/log_linux.c',
         'src/core/lib/support/log_posix.c',
         'src/core/lib/support/log_windows.c',
+        'src/core/lib/support/mpscq.c',
         'src/core/lib/support/murmur_hash.c',
+        'src/core/lib/support/percent_encoding.c',
         'src/core/lib/support/slice.c',
         'src/core/lib/support/slice_buffer.c',
         'src/core/lib/support/stack_lockfree.c',
@@ -568,9 +570,11 @@
         'src/core/lib/channel/channel_stack_builder.c',
         'src/core/lib/channel/compress_filter.c',
         'src/core/lib/channel/connected_channel.c',
+        'src/core/lib/channel/deadline_filter.c',
         'src/core/lib/channel/handshaker.c',
         'src/core/lib/channel/http_client_filter.c',
         'src/core/lib/channel/http_server_filter.c',
+        'src/core/lib/channel/message_size_filter.c',
         'src/core/lib/compression/compression.c',
         'src/core/lib/compression/message_compress.c',
         'src/core/lib/debug/trace.c',
@@ -578,6 +582,7 @@
         'src/core/lib/http/httpcli.c',
         'src/core/lib/http/parser.c',
         'src/core/lib/iomgr/closure.c',
+        'src/core/lib/iomgr/combiner.c',
         'src/core/lib/iomgr/endpoint.c',
         'src/core/lib/iomgr/endpoint_pair_posix.c',
         'src/core/lib/iomgr/endpoint_pair_windows.c',
@@ -649,6 +654,7 @@
         'src/core/lib/transport/metadata.c',
         'src/core/lib/transport/metadata_batch.c',
         'src/core/lib/transport/static_metadata.c',
+        'src/core/lib/transport/timeout_encoding.c',
         'src/core/lib/transport/transport.c',
         'src/core/lib/transport/transport_op_string.c',
         'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c',
@@ -671,7 +677,6 @@
         'src/core/ext/transport/chttp2/transport/status_conversion.c',
         'src/core/ext/transport/chttp2/transport/stream_lists.c',
         'src/core/ext/transport/chttp2/transport/stream_map.c',
-        'src/core/ext/transport/chttp2/transport/timeout_encoding.c',
         'src/core/ext/transport/chttp2/transport/varint.c',
         'src/core/ext/transport/chttp2/transport/writing.c',
         'src/core/ext/transport/chttp2/alpn/alpn.c',
@@ -707,10 +712,10 @@
         'src/core/ext/client_config/channel_connectivity.c',
         'src/core/ext/client_config/client_channel.c',
         'src/core/ext/client_config/client_channel_factory.c',
-        'src/core/ext/client_config/client_config.c',
         'src/core/ext/client_config/client_config_plugin.c',
         'src/core/ext/client_config/connector.c',
         'src/core/ext/client_config/default_initial_connect_string.c',
+        'src/core/ext/client_config/http_connect_handshaker.c',
         'src/core/ext/client_config/initial_connect_string.c',
         'src/core/ext/client_config/lb_policy.c',
         'src/core/ext/client_config/lb_policy_factory.c',
@@ -719,14 +724,15 @@
         'src/core/ext/client_config/resolver.c',
         'src/core/ext/client_config/resolver_factory.c',
         'src/core/ext/client_config/resolver_registry.c',
+        'src/core/ext/client_config/resolver_result.c',
         'src/core/ext/client_config/subchannel.c',
-        'src/core/ext/client_config/subchannel_call_holder.c',
         'src/core/ext/client_config/subchannel_index.c',
         'src/core/ext/client_config/uri_parser.c',
         'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c',
         'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
         'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
         'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c',
+        'src/core/ext/lb_policy/grpclb/grpclb.c',
         'src/core/ext/lb_policy/grpclb/load_balancer_api.c',
         'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
         'third_party/nanopb/pb_common.c',
@@ -738,8 +744,10 @@
         'src/core/ext/resolver/sockaddr/sockaddr_resolver.c',
         'src/core/ext/load_reporting/load_reporting.c',
         'src/core/ext/load_reporting/load_reporting_filter.c',
+        'src/core/ext/census/base_resources.c',
         'src/core/ext/census/context.c',
         'src/core/ext/census/gen/census.pb.c',
+        'src/core/ext/census/gen/trace_context.pb.c',
         'src/core/ext/census/grpc_context.c',
         'src/core/ext/census/grpc_filter.c',
         'src/core/ext/census/grpc_plugin.c',
@@ -747,6 +755,8 @@
         'src/core/ext/census/mlog.c',
         'src/core/ext/census/operation.c',
         'src/core/ext/census/placeholders.c',
+        'src/core/ext/census/resource.c',
+        'src/core/ext/census/trace_context.c',
         'src/core/ext/census/tracing.c',
         'src/core/plugin_registry/grpc_plugin_registry.c',
       ],
diff --git a/build.yaml b/build.yaml
index 37c95cc..7a70f0d 100644
--- a/build.yaml
+++ b/build.yaml
@@ -2,11 +2,17 @@
 '#2': It is used among other things to generate all of our project files.
 '#3': Please refer to the templates directory for more information.
 settings:
-  '#1': The public version number of the library.
-  '#2': Master always has a "-dev" suffix
-  '#3': Use "-preN" suffixes to identify pre-release versions
-  '#4': Per-language overrides are possible with (eg) ruby_version tag here
-  '#5': See the expand_version.py for all the quirks here
+  '#01': The public version number of the library.
+  '#02': ===
+  '#03': Please update the 'g_stands_for' field periodically with a new g word
+  '#04': not listed in doc/g_stands_for.md - and update that document to list the
+  '#05': new word.
+  '#06': ===
+  '#07': Master always has a "-dev" suffix
+  '#08': Use "-preN" suffixes to identify pre-release versions
+  '#09': Per-language overrides are possible with (eg) ruby_version tag here
+  '#10': See the expand_version.py for all the quirks here
+  g_stands_for: good
   version: 1.1.0-dev
 filegroups:
 - name: census
@@ -14,15 +20,21 @@
   - include/grpc/census.h
   headers:
   - src/core/ext/census/aggregation.h
+  - src/core/ext/census/base_resources.h
   - src/core/ext/census/census_interface.h
   - src/core/ext/census/census_rpc_stats.h
   - src/core/ext/census/gen/census.pb.h
+  - src/core/ext/census/gen/trace_context.pb.h
   - src/core/ext/census/grpc_filter.h
   - src/core/ext/census/mlog.h
+  - src/core/ext/census/resource.h
   - src/core/ext/census/rpc_metric_id.h
+  - src/core/ext/census/trace_context.h
   src:
+  - src/core/ext/census/base_resources.c
   - src/core/ext/census/context.c
   - src/core/ext/census/gen/census.pb.c
+  - src/core/ext/census/gen/trace_context.pb.c
   - src/core/ext/census/grpc_context.c
   - src/core/ext/census/grpc_filter.c
   - src/core/ext/census/grpc_plugin.c
@@ -30,6 +42,8 @@
   - src/core/ext/census/mlog.c
   - src/core/ext/census/operation.c
   - src/core/ext/census/placeholders.c
+  - src/core/ext/census/resource.c
+  - src/core/ext/census/trace_context.c
   - src/core/ext/census/tracing.c
   plugin: census_grpc_plugin
   uses:
@@ -70,7 +84,9 @@
   - src/core/lib/support/backoff.h
   - src/core/lib/support/block_annotate.h
   - src/core/lib/support/env.h
+  - src/core/lib/support/mpscq.h
   - src/core/lib/support/murmur_hash.h
+  - src/core/lib/support/percent_encoding.h
   - src/core/lib/support/stack_lockfree.h
   - src/core/lib/support/string.h
   - src/core/lib/support/string_windows.h
@@ -98,7 +114,9 @@
   - src/core/lib/support/log_linux.c
   - src/core/lib/support/log_posix.c
   - src/core/lib/support/log_windows.c
+  - src/core/lib/support/mpscq.c
   - src/core/lib/support/murmur_hash.c
+  - src/core/lib/support/percent_encoding.c
   - src/core/lib/support/slice.c
   - src/core/lib/support/slice_buffer.c
   - src/core/lib/support/stack_lockfree.c
@@ -127,20 +145,17 @@
   - gpr_codegen
 - name: gpr_codegen
   public_headers:
-  - include/grpc/impl/codegen/alloc.h
   - include/grpc/impl/codegen/atm.h
   - include/grpc/impl/codegen/atm_gcc_atomic.h
   - include/grpc/impl/codegen/atm_gcc_sync.h
   - include/grpc/impl/codegen/atm_windows.h
-  - include/grpc/impl/codegen/log.h
+  - include/grpc/impl/codegen/gpr_types.h
   - include/grpc/impl/codegen/port_platform.h
   - include/grpc/impl/codegen/slice.h
-  - include/grpc/impl/codegen/slice_buffer.h
   - include/grpc/impl/codegen/sync.h
   - include/grpc/impl/codegen/sync_generic.h
   - include/grpc/impl/codegen/sync_posix.h
   - include/grpc/impl/codegen/sync_windows.h
-  - include/grpc/impl/codegen/time.h
 - name: grpc_base
   public_headers:
   - include/grpc/byte_buffer.h
@@ -148,6 +163,7 @@
   - include/grpc/compression.h
   - include/grpc/grpc.h
   - include/grpc/grpc_posix.h
+  - include/grpc/grpc_security_constants.h
   - include/grpc/status.h
   headers:
   - src/core/lib/channel/channel_args.h
@@ -156,9 +172,11 @@
   - src/core/lib/channel/compress_filter.h
   - src/core/lib/channel/connected_channel.h
   - src/core/lib/channel/context.h
+  - src/core/lib/channel/deadline_filter.h
   - src/core/lib/channel/handshaker.h
   - src/core/lib/channel/http_client_filter.h
   - src/core/lib/channel/http_server_filter.h
+  - src/core/lib/channel/message_size_filter.h
   - src/core/lib/compression/algorithm_metadata.h
   - src/core/lib/compression/message_compress.h
   - src/core/lib/debug/trace.h
@@ -166,6 +184,7 @@
   - src/core/lib/http/httpcli.h
   - src/core/lib/http/parser.h
   - src/core/lib/iomgr/closure.h
+  - src/core/lib/iomgr/combiner.h
   - src/core/lib/iomgr/endpoint.h
   - src/core/lib/iomgr/endpoint_pair.h
   - src/core/lib/iomgr/error.h
@@ -227,6 +246,7 @@
   - src/core/lib/transport/metadata.h
   - src/core/lib/transport/metadata_batch.h
   - src/core/lib/transport/static_metadata.h
+  - src/core/lib/transport/timeout_encoding.h
   - src/core/lib/transport/transport.h
   - src/core/lib/transport/transport_impl.h
   src:
@@ -235,9 +255,11 @@
   - src/core/lib/channel/channel_stack_builder.c
   - src/core/lib/channel/compress_filter.c
   - src/core/lib/channel/connected_channel.c
+  - src/core/lib/channel/deadline_filter.c
   - src/core/lib/channel/handshaker.c
   - src/core/lib/channel/http_client_filter.c
   - src/core/lib/channel/http_server_filter.c
+  - src/core/lib/channel/message_size_filter.c
   - src/core/lib/compression/compression.c
   - src/core/lib/compression/message_compress.c
   - src/core/lib/debug/trace.c
@@ -245,6 +267,7 @@
   - src/core/lib/http/httpcli.c
   - src/core/lib/http/parser.c
   - src/core/lib/iomgr/closure.c
+  - src/core/lib/iomgr/combiner.c
   - src/core/lib/iomgr/endpoint.c
   - src/core/lib/iomgr/endpoint_pair_posix.c
   - src/core/lib/iomgr/endpoint_pair_windows.c
@@ -316,6 +339,7 @@
   - src/core/lib/transport/metadata.c
   - src/core/lib/transport/metadata_batch.c
   - src/core/lib/transport/static_metadata.c
+  - src/core/lib/transport/timeout_encoding.c
   - src/core/lib/transport/transport.c
   - src/core/lib/transport/transport_op_string.c
   deps:
@@ -326,8 +350,8 @@
   headers:
   - src/core/ext/client_config/client_channel.h
   - src/core/ext/client_config/client_channel_factory.h
-  - src/core/ext/client_config/client_config.h
   - src/core/ext/client_config/connector.h
+  - src/core/ext/client_config/http_connect_handshaker.h
   - src/core/ext/client_config/initial_connect_string.h
   - src/core/ext/client_config/lb_policy.h
   - src/core/ext/client_config/lb_policy_factory.h
@@ -336,18 +360,18 @@
   - src/core/ext/client_config/resolver.h
   - src/core/ext/client_config/resolver_factory.h
   - src/core/ext/client_config/resolver_registry.h
+  - src/core/ext/client_config/resolver_result.h
   - src/core/ext/client_config/subchannel.h
-  - src/core/ext/client_config/subchannel_call_holder.h
   - src/core/ext/client_config/subchannel_index.h
   - src/core/ext/client_config/uri_parser.h
   src:
   - src/core/ext/client_config/channel_connectivity.c
   - src/core/ext/client_config/client_channel.c
   - src/core/ext/client_config/client_channel_factory.c
-  - src/core/ext/client_config/client_config.c
   - src/core/ext/client_config/client_config_plugin.c
   - src/core/ext/client_config/connector.c
   - src/core/ext/client_config/default_initial_connect_string.c
+  - src/core/ext/client_config/http_connect_handshaker.c
   - src/core/ext/client_config/initial_connect_string.c
   - src/core/ext/client_config/lb_policy.c
   - src/core/ext/client_config/lb_policy_factory.c
@@ -356,8 +380,8 @@
   - src/core/ext/client_config/resolver.c
   - src/core/ext/client_config/resolver_factory.c
   - src/core/ext/client_config/resolver_registry.c
+  - src/core/ext/client_config/resolver_result.c
   - src/core/ext/client_config/subchannel.c
-  - src/core/ext/client_config/subchannel_call_holder.c
   - src/core/ext/client_config/subchannel_index.c
   - src/core/ext/client_config/uri_parser.c
   plugin: grpc_client_config
@@ -365,7 +389,6 @@
   - grpc_base
 - name: grpc_codegen
   public_headers:
-  - include/grpc/impl/codegen/byte_buffer.h
   - include/grpc/impl/codegen/byte_buffer_reader.h
   - include/grpc/impl/codegen/compression_types.h
   - include/grpc/impl/codegen/connectivity_state.h
@@ -376,11 +399,14 @@
   - gpr_codegen
 - name: grpc_lb_policy_grpclb
   headers:
+  - src/core/ext/lb_policy/grpclb/grpclb.h
   - src/core/ext/lb_policy/grpclb/load_balancer_api.h
   - src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h
   src:
+  - src/core/ext/lb_policy/grpclb/grpclb.c
   - src/core/ext/lb_policy/grpclb/load_balancer_api.c
   - src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
+  plugin: grpc_lb_policy_grpclb
   uses:
   - grpc_base
   - grpc_client_config
@@ -426,7 +452,6 @@
 - name: grpc_secure
   public_headers:
   - include/grpc/grpc_security.h
-  - include/grpc/grpc_security_constants.h
   headers:
   - src/core/lib/security/context/security_context.h
   - src/core/lib/security/credentials/composite/composite_credentials.h
@@ -482,6 +507,8 @@
   build: test
   headers:
   - test/core/end2end/cq_verifier.h
+  - test/core/end2end/fake_resolver.h
+  - test/core/end2end/fixtures/http_proxy.h
   - test/core/end2end/fixtures/proxy.h
   - test/core/iomgr/endpoint_tests.h
   - test/core/util/grpc_profiler.h
@@ -494,6 +521,8 @@
   - test/core/util/slice_splitter.h
   src:
   - test/core/end2end/cq_verifier.c
+  - test/core/end2end/fake_resolver.c
+  - test/core/end2end/fixtures/http_proxy.c
   - test/core/end2end/fixtures/proxy.c
   - test/core/iomgr/endpoint_tests.c
   - test/core/util/grpc_profiler.c
@@ -529,7 +558,6 @@
   - src/core/ext/transport/chttp2/transport/internal.h
   - src/core/ext/transport/chttp2/transport/status_conversion.h
   - src/core/ext/transport/chttp2/transport/stream_map.h
-  - src/core/ext/transport/chttp2/transport/timeout_encoding.h
   - src/core/ext/transport/chttp2/transport/varint.h
   src:
   - src/core/ext/transport/chttp2/transport/bin_decoder.c
@@ -551,7 +579,6 @@
   - src/core/ext/transport/chttp2/transport/status_conversion.c
   - src/core/ext/transport/chttp2/transport/stream_lists.c
   - src/core/ext/transport/chttp2/transport/stream_map.c
-  - src/core/ext/transport/chttp2/transport/timeout_encoding.c
   - src/core/ext/transport/chttp2/transport/varint.c
   - src/core/ext/transport/chttp2/transport/writing.c
   plugin: grpc_chttp2_plugin
@@ -685,37 +712,35 @@
   - include/grpc++/support/time.h
   headers:
   - src/cpp/client/create_channel_internal.h
+  - src/cpp/common/channel_filter.h
   - src/cpp/server/dynamic_thread_pool.h
   - src/cpp/server/thread_pool_interface.h
   src:
-  - src/cpp/client/channel.cc
+  - src/cpp/client/channel_cc.cc
   - src/cpp/client/client_context.cc
   - src/cpp/client/create_channel.cc
   - src/cpp/client/create_channel_internal.cc
   - src/cpp/client/create_channel_posix.cc
-  - src/cpp/client/credentials.cc
+  - src/cpp/client/credentials_cc.cc
   - src/cpp/client/generic_stub.cc
-  - src/cpp/client/insecure_credentials.cc
   - src/cpp/common/channel_arguments.cc
-  - src/cpp/common/completion_queue.cc
+  - src/cpp/common/channel_filter.cc
+  - src/cpp/common/completion_queue_cc.cc
   - src/cpp/common/core_codegen.cc
   - src/cpp/common/rpc_method.cc
   - src/cpp/server/async_generic_service.cc
   - src/cpp/server/create_default_thread_pool.cc
   - src/cpp/server/dynamic_thread_pool.cc
-  - src/cpp/server/insecure_server_credentials.cc
-  - src/cpp/server/server.cc
   - src/cpp/server/server_builder.cc
+  - src/cpp/server/server_cc.cc
   - src/cpp/server/server_context.cc
   - src/cpp/server/server_credentials.cc
   - src/cpp/server/server_posix.cc
-  - src/cpp/util/byte_buffer.cc
-  - src/cpp/util/slice.cc
+  - src/cpp/util/byte_buffer_cc.cc
+  - src/cpp/util/slice_cc.cc
   - src/cpp/util/status.cc
   - src/cpp/util/string_ref.cc
-  - src/cpp/util/time.cc
-  deps:
-  - grpc
+  - src/cpp/util/time_cc.cc
   uses:
   - grpc++_codegen_base
 - name: grpc++_codegen_base
@@ -744,6 +769,7 @@
   - include/grpc++/impl/codegen/service_type.h
   - include/grpc++/impl/codegen/status.h
   - include/grpc++/impl/codegen/status_code_enum.h
+  - include/grpc++/impl/codegen/status_helper.h
   - include/grpc++/impl/codegen/string_ref.h
   - include/grpc++/impl/codegen/stub_options.h
   - include/grpc++/impl/codegen/sync.h
@@ -780,6 +806,13 @@
   - src/cpp/ext/reflection.pb.cc
   uses:
   - grpc++_codegen_proto
+- name: thrift_util
+  language: c++
+  public_headers:
+  - include/grpc++/impl/codegen/thrift_serializer.h
+  - include/grpc++/impl/codegen/thrift_utils.h
+  uses:
+  - grpc++_codegen_base
 libs:
 - name: gpr
   build: all
@@ -882,6 +915,7 @@
   - grpc
   filegroups:
   - grpc_test_util_base
+  - grpc_base
   vs_project_guid: '{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}'
 - name: grpc_test_util_unsecure
   build: private
@@ -951,11 +985,13 @@
   - src/cpp/common/secure_auth_context.h
   - src/cpp/server/secure_server_credentials.h
   src:
+  - src/cpp/client/insecure_credentials.cc
   - src/cpp/client/secure_credentials.cc
   - src/cpp/common/auth_property_iterator.cc
   - src/cpp/common/secure_auth_context.cc
   - src/cpp/common/secure_channel_arguments.cc
   - src/cpp/common/secure_create_auth_context.cc
+  - src/cpp/server/insecure_server_credentials.cc
   - src/cpp/server/secure_server_credentials.cc
   deps:
   - grpc
@@ -992,7 +1028,7 @@
   headers:
   - test/cpp/util/test_config.h
   src:
-  - test/cpp/util/test_config.cc
+  - test/cpp/util/test_config_cc.cc
 - name: grpc++_test_util
   build: private
   language: c++
@@ -1021,11 +1057,14 @@
   - grpc++_codegen_base_src
   - grpc++_codegen_proto
   - grpc++_config_proto
+  - thrift_util
 - name: grpc++_unsecure
   build: all
   language: c++
   src:
+  - src/cpp/client/insecure_credentials.cc
   - src/cpp/common/insecure_create_auth_context.cc
+  - src/cpp/server/insecure_server_credentials.cc
   deps:
   - gpr
   - grpc_unsecure
@@ -1042,16 +1081,23 @@
   language: c++
   headers:
   - test/cpp/util/cli_call.h
+  - test/cpp/util/cli_credentials.h
+  - test/cpp/util/config_grpc_cli.h
+  - test/cpp/util/grpc_tool.h
   - test/cpp/util/proto_file_parser.h
   - test/cpp/util/proto_reflection_descriptor_database.h
+  - test/cpp/util/service_describer.h
   src:
   - test/cpp/util/cli_call.cc
+  - test/cpp/util/cli_credentials.cc
+  - test/cpp/util/grpc_tool.cc
   - test/cpp/util/proto_file_parser.cc
   - test/cpp/util/proto_reflection_descriptor_database.cc
+  - test/cpp/util/service_describer.cc
   deps:
   - grpc++_reflection
   - grpc++
-  - grpc_plugin_support
+  - grpc++_test_config
 - name: grpc_plugin_support
   build: protoc
   language: c++
@@ -1066,6 +1112,8 @@
   - src/compiler/node_generator_helpers.h
   - src/compiler/objective_c_generator.h
   - src/compiler/objective_c_generator_helpers.h
+  - src/compiler/php_generator.h
+  - src/compiler/php_generator_helpers.h
   - src/compiler/python_generator.h
   - src/compiler/ruby_generator.h
   - src/compiler/ruby_generator_helpers-inl.h
@@ -1076,6 +1124,7 @@
   - src/compiler/csharp_generator.cc
   - src/compiler/node_generator.cc
   - src/compiler/objective_c_generator.cc
+  - src/compiler/php_generator.cc
   - src/compiler/python_generator.cc
   - src/compiler/ruby_generator.cc
   filegroups:
@@ -1130,7 +1179,7 @@
   - grpc++
   - grpc
   - gpr
-- name: interop_server_main
+- name: interop_server_lib
   build: private
   language: c++
   src:
@@ -1147,6 +1196,13 @@
   - gpr_test_util
   - gpr
   - grpc++_test_config
+- name: interop_server_main
+  build: private
+  language: c++
+  src:
+  - test/cpp/interop/interop_server_bootstrap.cc
+  deps:
+  - interop_server_lib
 - name: qps
   build: private
   language: c++
@@ -1295,6 +1351,26 @@
   - grpc
   - gpr_test_util
   - gpr
+- name: census_resource_test
+  build: test
+  language: c
+  src:
+  - test/core/census/resource_test.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+- name: census_trace_context_test
+  build: test
+  language: c
+  src:
+  - test/core/census/trace_context_test.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
 - name: channel_create_test
   build: test
   language: c
@@ -1359,6 +1435,17 @@
   - test/core/end2end/fuzzers/client_fuzzer_corpus
   dict: test/core/end2end/fuzzers/hpack.dictionary
   maxlen: 2048
+- name: combiner_test
+  cpu_cost: 30
+  build: test
+  language: c
+  src:
+  - test/core/iomgr/combiner_test.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
 - name: compression_test
   build: test
   language: c
@@ -1379,6 +1466,17 @@
   - grpc
   - gpr_test_util
   - gpr
+- name: connection_refused_test
+  cpu_cost: 0.1
+  build: test
+  language: c
+  src:
+  - test/core/end2end/connection_refused_test.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
 - name: dns_resolver_connectivity_test
   cpu_cost: 0.1
   build: test
@@ -1531,6 +1629,12 @@
   src:
   - tools/codegen/core/gen_legal_metadata_characters.c
   deps: []
+- name: gen_percent_encoding_tables
+  build: tool
+  language: c
+  src:
+  - tools/codegen/core/gen_percent_encoding_tables.c
+  deps: []
 - name: goaway_server_test
   cpu_cost: 0.1
   build: test
@@ -1610,6 +1714,23 @@
   deps:
   - gpr_test_util
   - gpr
+- name: gpr_mpscq_test
+  cpu_cost: 30
+  build: test
+  language: c
+  src:
+  - test/core/support/mpscq_test.c
+  deps:
+  - gpr_test_util
+  - gpr
+- name: gpr_percent_encoding_test
+  build: test
+  language: c
+  src:
+  - test/core/support/percent_encoding_test.c
+  deps:
+  - gpr_test_util
+  - gpr
 - name: gpr_slice_buffer_test
   build: test
   language: c
@@ -2171,6 +2292,32 @@
   - grpc
   - gpr_test_util
   - gpr
+- name: percent_decode_fuzzer
+  build: fuzzer
+  language: c
+  src:
+  - test/core/support/percent_decode_fuzzer.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+  corpus_dirs:
+  - test/core/support/percent_decode_corpus
+  maxlen: 32
+- name: percent_encode_fuzzer
+  build: fuzzer
+  language: c
+  src:
+  - test/core/support/percent_encode_fuzzer.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+  corpus_dirs:
+  - test/core/support/percent_encode_corpus
+  maxlen: 32
 - name: resolve_address_test
   build: test
   language: c
@@ -2349,7 +2496,7 @@
   build: test
   language: c
   src:
-  - test/core/transport/chttp2/timeout_encoding_test.c
+  - test/core/transport/timeout_encoding_test.c
   deps:
   - grpc_test_util
   - grpc
@@ -2639,6 +2786,19 @@
   - grpc
   - gpr_test_util
   - gpr
+- name: filter_end2end_test
+  gtest: true
+  build: test
+  language: c++
+  src:
+  - test/cpp/end2end/filter_end2end_test.cc
+  deps:
+  - grpc++_test_util
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - gpr_test_util
+  - gpr
 - name: generic_end2end_test
   gtest: true
   build: test
@@ -2671,12 +2831,9 @@
   - test/cpp/util/grpc_cli.cc
   deps:
   - grpc_cli_libs
-  - grpc++_test_util
-  - grpc_test_util
   - grpc++_reflection
   - grpc++
   - grpc
-  - gpr_test_util
   - gpr
   - grpc++_test_config
 - name: grpc_cpp_plugin
@@ -2718,6 +2875,15 @@
   secure: false
   vs_config_type: Application
   vs_project_guid: '{19564640-CEE6-4921-ABA5-676ED79A36F6}'
+- name: grpc_php_plugin
+  build: protoc
+  language: c++
+  src:
+  - src/compiler/php_plugin.cc
+  deps:
+  - grpc_plugin_support
+  secure: false
+  vs_config_type: Application
 - name: grpc_python_plugin
   build: protoc
   language: c++
@@ -2738,6 +2904,28 @@
   secure: false
   vs_config_type: Application
   vs_project_guid: '{069E9D05-B78B-4751-9252-D21EBAE7DE8E}'
+- name: grpc_tool_test
+  gtest: true
+  build: test
+  language: c++
+  headers:
+  - test/cpp/util/string_ref_helper.h
+  src:
+  - src/proto/grpc/testing/echo.proto
+  - src/proto/grpc/testing/echo_messages.proto
+  - test/cpp/util/grpc_tool_test.cc
+  - test/cpp/util/string_ref_helper.cc
+  deps:
+  - grpc_cli_libs
+  - grpc++_reflection
+  - grpc_test_util
+  - grpc++
+  - grpc
+  - gpr_test_util
+  - gpr
+  filegroups:
+  - grpc++_codegen_proto
+  - grpc++_config_proto
 - name: grpclb_api_test
   gtest: true
   build: test
@@ -2750,6 +2938,20 @@
   - grpc_test_util
   - grpc++
   - grpc
+- name: grpclb_test
+  gtest: false
+  build: test
+  language: c++
+  src:
+  - src/proto/grpc/lb/v1/load_balancer.proto
+  - test/cpp/grpclb/grpclb_test.cc
+  deps:
+  - gpr
+  - gpr_test_util
+  - grpc
+  - grpc++
+  - grpc++_test_util
+  - grpc_test_util
 - name: hybrid_end2end_test
   gtest: true
   build: test
@@ -2790,6 +2992,7 @@
   deps:
   - interop_server_main
   - interop_server_helper
+  - interop_server_lib
   - grpc++_test_util
   - grpc_test_util
   - grpc++
diff --git a/composer.json b/composer.json
index 7836620..c5c7ae8 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
   "license": "BSD-3-Clause",
   "require": {
     "php": ">=5.5.0",
-    "stanley-cheung/protobuf-php": "v0.6"
+    "google/protobuf": "v3.1.0-alpha-1"
   },
   "require-dev": {
     "google/auth": "v0.9"
diff --git a/config.m4 b/config.m4
index e4e4108..0f10365 100644
--- a/config.m4
+++ b/config.m4
@@ -56,7 +56,9 @@
     src/core/lib/support/log_linux.c \
     src/core/lib/support/log_posix.c \
     src/core/lib/support/log_windows.c \
+    src/core/lib/support/mpscq.c \
     src/core/lib/support/murmur_hash.c \
+    src/core/lib/support/percent_encoding.c \
     src/core/lib/support/slice.c \
     src/core/lib/support/slice_buffer.c \
     src/core/lib/support/stack_lockfree.c \
@@ -87,9 +89,11 @@
     src/core/lib/channel/channel_stack_builder.c \
     src/core/lib/channel/compress_filter.c \
     src/core/lib/channel/connected_channel.c \
+    src/core/lib/channel/deadline_filter.c \
     src/core/lib/channel/handshaker.c \
     src/core/lib/channel/http_client_filter.c \
     src/core/lib/channel/http_server_filter.c \
+    src/core/lib/channel/message_size_filter.c \
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/debug/trace.c \
@@ -97,6 +101,7 @@
     src/core/lib/http/httpcli.c \
     src/core/lib/http/parser.c \
     src/core/lib/iomgr/closure.c \
+    src/core/lib/iomgr/combiner.c \
     src/core/lib/iomgr/endpoint.c \
     src/core/lib/iomgr/endpoint_pair_posix.c \
     src/core/lib/iomgr/endpoint_pair_windows.c \
@@ -168,6 +173,7 @@
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
     src/core/lib/transport/static_metadata.c \
+    src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
     src/core/lib/transport/transport_op_string.c \
     src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c \
@@ -190,7 +196,6 @@
     src/core/ext/transport/chttp2/transport/status_conversion.c \
     src/core/ext/transport/chttp2/transport/stream_lists.c \
     src/core/ext/transport/chttp2/transport/stream_map.c \
-    src/core/ext/transport/chttp2/transport/timeout_encoding.c \
     src/core/ext/transport/chttp2/transport/varint.c \
     src/core/ext/transport/chttp2/transport/writing.c \
     src/core/ext/transport/chttp2/alpn/alpn.c \
@@ -226,10 +231,10 @@
     src/core/ext/client_config/channel_connectivity.c \
     src/core/ext/client_config/client_channel.c \
     src/core/ext/client_config/client_channel_factory.c \
-    src/core/ext/client_config/client_config.c \
     src/core/ext/client_config/client_config_plugin.c \
     src/core/ext/client_config/connector.c \
     src/core/ext/client_config/default_initial_connect_string.c \
+    src/core/ext/client_config/http_connect_handshaker.c \
     src/core/ext/client_config/initial_connect_string.c \
     src/core/ext/client_config/lb_policy.c \
     src/core/ext/client_config/lb_policy_factory.c \
@@ -238,14 +243,15 @@
     src/core/ext/client_config/resolver.c \
     src/core/ext/client_config/resolver_factory.c \
     src/core/ext/client_config/resolver_registry.c \
+    src/core/ext/client_config/resolver_result.c \
     src/core/ext/client_config/subchannel.c \
-    src/core/ext/client_config/subchannel_call_holder.c \
     src/core/ext/client_config/subchannel_index.c \
     src/core/ext/client_config/uri_parser.c \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \
     src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create.c \
     src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \
+    src/core/ext/lb_policy/grpclb/grpclb.c \
     src/core/ext/lb_policy/grpclb/load_balancer_api.c \
     src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
     third_party/nanopb/pb_common.c \
@@ -257,8 +263,10 @@
     src/core/ext/resolver/sockaddr/sockaddr_resolver.c \
     src/core/ext/load_reporting/load_reporting.c \
     src/core/ext/load_reporting/load_reporting_filter.c \
+    src/core/ext/census/base_resources.c \
     src/core/ext/census/context.c \
     src/core/ext/census/gen/census.pb.c \
+    src/core/ext/census/gen/trace_context.pb.c \
     src/core/ext/census/grpc_context.c \
     src/core/ext/census/grpc_filter.c \
     src/core/ext/census/grpc_plugin.c \
@@ -266,6 +274,8 @@
     src/core/ext/census/mlog.c \
     src/core/ext/census/operation.c \
     src/core/ext/census/placeholders.c \
+    src/core/ext/census/resource.c \
+    src/core/ext/census/trace_context.c \
     src/core/ext/census/tracing.c \
     src/core/plugin_registry/grpc_plugin_registry.c \
     src/boringssl/err_data.c \
diff --git a/doc/PROTOCOL-HTTP2.md b/doc/PROTOCOL-HTTP2.md
index 31d694b..18803f2 100644
--- a/doc/PROTOCOL-HTTP2.md
+++ b/doc/PROTOCOL-HTTP2.md
@@ -98,8 +98,11 @@
 * **Trailers-Only** → HTTP-Status Content-Type Trailers
 * **Trailers** → Status [Status-Message] \*Custom-Metadata
 * **HTTP-Status** → ":status 200"
-* **Status** → "grpc-status" <status-code-as-ASCII-string>
-* **Status-Message** → "grpc-message" <descriptive text for status as ASCII string>
+* **Status** → "grpc-status" 1\*DIGIT ; 0-9
+* **Status-Message** → "grpc-message" Percent-Encoded
+* **Percent-Encoded** → 1\*(Percent-Byte-Unencoded / Percent-Byte-Encoded)
+* **Percent-Byte-Unencoded** → 1\*( %x20-%x24 / %x26-%x7E ) ; space and VCHAR, except %
+* **Percent-Byte-Encoded** → "%" 2HEXDIGIT ; 0-9 A-F
 
 **Response-Headers** & **Trailers-Only** are each delivered in a single HTTP2 HEADERS frame block. Most responses are expected to have both headers and trailers but **Trailers-Only** is permitted for calls that produce an immediate error. Status must be sent in **Trailers** even if the status code is OK.
 
@@ -110,6 +113,21 @@
 Clients may limit the size of **Response-Headers**, **Trailers**, and
 **Trailers-Only**, with a default of 8 KiB each suggested.
 
+The value portion of **Status** is a decimal-encoded integer as an ASCII string,
+without any leading zeros.
+
+The value portion of **Status-Message** is conceptually a Unicode string
+description of the error, physically encoded as UTF-8 followed by
+percent-encoding. Percent-encoding is specified in [RFC 3986
+§2.1](https://tools.ietf.org/html/rfc3986#section-2.1), although the form used
+here has different restricted characters. When decoding invalid values,
+implementations MUST NOT error or throw away the message. At worst, the
+implementation can abort decoding the status message altogether such that the
+user would received the raw percent-encoded form. Alternatively, the
+implementation can decode valid portions while leaving broken %-encodings as-is
+or replacing them with a replacement character (e.g., '?' or the Unicode
+replacement character).
+
 ####Example
 
 Sample unary-call showing HTTP2 framing sequence
@@ -157,6 +175,16 @@
 grpc-ruby-jruby/1.3.4
 grpc-java-android/0.9.1 (gingerbread/1.2.4; nexus5; tmobile)
 ```
+
+####Idempotency and Retries
+
+Unless explicitly defined to be, gRPC Calls are not assumed to be idempotent.  Specifically:
+
+* Calls that cannot be proven to have started will not be retried.
+* There is no mechanisim for duplicate suppression as it is not necessary.
+* Calls that are marked as idempotent may be sent multiple times.
+
+
 ####HTTP2 Transport Mapping
 
 #####Stream Identification
diff --git a/doc/core/pending_api_cleanups.md b/doc/core/pending_api_cleanups.md
new file mode 100644
index 0000000..a12e8a9
--- /dev/null
+++ b/doc/core/pending_api_cleanups.md
@@ -0,0 +1,17 @@
+There are times when we make changes that include a temporary shim for
+backward-compatibility (e.g., a macro or some other function to preserve
+the original API) to avoid having to bump the major version number in
+the next release.  However, when we do eventually want to release a
+feature that does change the API in a non-backward-compatible way, we
+will wind up bumping the major version number anyway, at which point we
+can take the opportunity to clean up any pending backward-compatibility
+shims.
+
+This file lists all pending backward-compatibility changes that should
+be cleaned up the next time we are going to bump the major version
+number:
+
+- remove `GRPC_ARG_MAX_MESSAGE_LENGTH` channel arg from
+  `include/grpc/impl/codegen/grpc_types.h` (commit `af00d8b`)
+- remove `ServerBuilder::SetMaxMessageSize()` method from
+  `include/grpc++/server_builder.h` (commit `6980362`)
diff --git a/doc/environment_variables.md b/doc/environment_variables.md
new file mode 100644
index 0000000..74b7862
--- /dev/null
+++ b/doc/environment_variables.md
@@ -0,0 +1,67 @@
+gRPC environment variables
+--------------------------
+
+gRPC C core based implementations (those contained in this repository) expose
+some configuration as environment variables that can be set.
+
+* GRPC_ABORT_ON_LEAKS
+  A debugging aid to cause a call to abort() when gRPC objects are leaked past
+  grpc_shutdown(). Set to 1 to cause the abort, if unset or 0 it does not
+  abort the process.
+
+* GOOGLE_APPLICATION_CREDENTIALS
+  The path to find the credentials to use when Google credentials are created
+
+* GRPC_SSL_CIPHER_SUITES
+  A colon separated list of cipher suites to use with OpenSSL
+  Defaults to:
+    ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-GCM-SHA384
+
+* GRPC_DEFAULT_SSL_ROOTS_FILE_PATH
+  PEM file to load SSL roots from
+
+* GRPC_POLL_STRATEGY [posix-style environments only]
+  Declares which polling engines to try when starting gRPC.
+  This is a comma-separated list of engines, which are tried in priority order
+  first -> last.
+  Available polling engines include:
+  - epoll (linux-only) - a polling engine based around the epoll family of
+    system calls
+  - poll - a portable polling engine based around poll(), intended to be a
+    fallback engine when nothing better exists
+  - legacy - the (deprecated) original polling engine for gRPC
+
+* GRPC_TRACE
+  A comma separated list of tracers that provide additional insight into how
+  gRPC C core is processing requests via debug logs. Available tracers include:
+  - api - traces api calls to the C core
+  - channel - traces operations on the C core channel stack
+  - combiner - traces combiner lock state
+  - compression - traces compression operations
+  - connectivity_state - traces connectivity state changes to channels
+  - channel_stack_builder - traces information about channel stacks being built
+  - http - traces state in the http2 transport engine
+  - http1 - traces HTTP/1.x operations performed by gRPC
+  - flowctl - traces http2 flow control
+  - op_failure - traces error information when failure is pushed onto a
+    completion queue
+  - pending_tags - [debug builds only] traces still-in-progress tags on
+    completion queues
+  - round_robin - traces the round_robin load balancing policy
+  - glb - traces the grpclb load balancer
+  - queue_pluck
+  - queue_timeout
+  - secure_endpoint - traces bytes flowing through encrypted channels
+  - transport_security - traces metadata about secure channel establishment
+  - tcp - traces bytes in and out of a channel
+  'all' can additionally be used to turn all traces on.
+  Individual traces can be disabled by prefixing them with '-'.
+  Example:
+   export GRPC_TRACE=all,-pending_tags
+
+* GRPC_VERBOSITY
+  Default gRPC logging verbosity - one of:
+  - DEBUG - log all gRPC messages
+  - INFO - log INFO and ERROR message
+  - ERROR - log only errors
+
diff --git a/doc/fail_fast.md b/doc/fail_fast.md
index 3ed4297..2dd5561 100644
--- a/doc/fail_fast.md
+++ b/doc/fail_fast.md
@@ -1,15 +1 @@
-gRPC Fail Fast Semantics
-========================
-
-Fail fast requests allow terminating requests (with status UNAVAILABLE) prior
-to the deadline of the request being met.
-
-gRPC implementations of fail fast can terminate requests whenever a channel is
-in the TRANSIENT_FAILURE or SHUTDOWN states. If the channel is in any other
-state (CONNECTING, READY, or IDLE) the request should not be terminated.
-
-Fail fast SHOULD be the default for gRPC implementations, with an option to
-switch to non fail fast.
-
-The opposite of fail fast is 'ignore connectivity'.
-
+Moved to wait-for-ready.md
diff --git a/doc/g_stands_for.md b/doc/g_stands_for.md
new file mode 100644
index 0000000..52f8eae
--- /dev/null
+++ b/doc/g_stands_for.md
@@ -0,0 +1,8 @@
+Each version of gRPC gets a new description of what the 'g' stands for, since
+we've never really been able to figure it out.
+
+Below is a list of already-used definitions (that should not be repeated in the
+future), and the corresponding version numbers that used them:
+
+- 1.0 'g' stands for 'gRPC'
+- 1.1 'g' stands for 'good'
diff --git a/doc/health-checking.md b/doc/health-checking.md
index 92512e9..7be8107 100644
--- a/doc/health-checking.md
+++ b/doc/health-checking.md
@@ -58,7 +58,7 @@
 set to `SERVING` or `NOT_SERVING` accordingly. If the service name is not
 registered, the server returns a `NOT_FOUND` GRPC status.
 
-The server should use an empty string as the key for server’s
+The server should use an empty string as the key for server's
 overall health status, so that a client not interested in a specific service can
 query the server's status with an empty request. The server can just do exact
 matching of the service name without support of any kind of wildcard matching.
diff --git a/doc/http-grpc-status-mapping.md b/doc/http-grpc-status-mapping.md
new file mode 100644
index 0000000..928fe54
--- /dev/null
+++ b/doc/http-grpc-status-mapping.md
@@ -0,0 +1,30 @@
+# HTTP to gRPC Status Code Mapping
+
+Since intermediaries are a common part of HTTP infrastructure some responses to
+gRPC requests may be received that do not include the grpc-status header. In
+some cases mapping error codes from an intermediary allows the gRPC client to
+behave more appropriately to the error situation without overloading the
+semantics of either error code.
+
+This table is to be used _only_ for clients that received a response that did
+not include grpc-status. If grpc-status was provided, it _must_ be used. Servers
+_must not_ use this table to determine an HTTP status code to use; the mappings
+are neither symmetric nor 1-to-1.
+
+| HTTP Status Code           | gRPC Status Code   |
+|----------------------------|--------------------|
+| 400 Bad Request            | INTERNAL           |
+| 401 Unauthorized           | UNAUTHENTICATED    |
+| 403 Forbidden              | PERMISSION\_DENIED |
+| 404 Not Found              | UNIMPLEMENTED      |
+| 429 Too Many Requests      | UNAVAILABLE        |
+| 502 Bad Gateway            | UNAVAILABLE        |
+| 503 Service Unavailable    | UNAVAILABLE        |
+| 504 Gateway Timeout        | UNAVAILABLE        |
+| _All other codes_          | UNKNOWN            |
+
+Technically, 1xx should have the entire header skipped and a subsequent header
+be read. See RFC 7540 §8.1.
+
+200 is UNKNOWN because there should be a grpc-status in case of truly OK
+response.
diff --git a/doc/server_reflection_tutorial.md b/doc/server_reflection_tutorial.md
new file mode 100644
index 0000000..ecb1767
--- /dev/null
+++ b/doc/server_reflection_tutorial.md
@@ -0,0 +1,192 @@
+# gRPC Server Reflection Tutorial
+
+gRPC Server Reflection provides information about publicly-accessible gRPC
+services on a server, and assists clients at runtime to construct RPC
+requests and responses without precompiled service information. It is used by
+gRPC CLI, which can be used to introspect server protos and send/receive test
+RPCs.
+
+## Enable Server Reflection
+
+### Enable server reflection in C++ servers
+
+C++ Server Reflection is an add-on library, `libgrpc++_reflction`. To enable C++
+server reflection, you can link this library to your server binary.
+
+Some platforms (e.g. Ubuntu 11.10 onwards) only link in libraries that directly
+contain symbols used by the application. On these platforms, LD flag
+`--no-as-needed` is needed for for dynamic linking and `--whole-archive` is
+needed for for static linking.
+
+This [Makefile](../examples/cpp/helloworld/Makefile#L37#L45) demonstrates
+enabling c++ server reflection on Linux and MacOS.
+
+## Test services using Server Reflection
+
+After enabling Server Reflection in a server application, you can use gRPC CLI
+to test its services.
+
+Instructions on how to use gRPC CLI can be found at
+[command_line_tool.md](command_line_tool.md), or using `grpc_cli help` command.
+
+Here we use `examples/cpp/helloworld` as an example to show the use of gRPC
+Server Reflection and gRPC CLI. First, we need to build gRPC CLI and setup an
+example server with Server Reflection enabled.
+
+- Setup an example server
+
+  Server Reflection has already been enabled in the
+  [Makefile](../examples/cpp/helloworld/Makefile) of the helloworld example. We
+  can simply make it and run the greeter_server.
+
+  ```sh
+  $ make -C examples/cpp/helloworld
+  $ examples/cpp/helloworld/greeter_server &
+  ```
+
+- Build gRPC CLI
+
+  ```sh
+  make grpc_cli
+  cd bins/opt
+  ```
+
+  gRPC CLI binary `grpc_cli` can be found at `bins/opt/` folder. This tool is
+  still new and does not have a `make install` target yet.
+
+### List services
+
+`grpc_cli ls` command lists services and methods exposed at a given port
+
+- List all the services exposed at a given port
+
+  ```sh
+  $ grpc_cli ls localhost:50051
+  ```
+
+  output:
+  ```sh
+  helloworld.Greeter
+  grpc.reflection.v1alpha.ServerReflection
+  ```
+
+- List one service with details
+
+  `grpc_cli ls` command inspects a service given its full name (in the format of
+  \<package\>.\<service\>). It can print information with a long listing format
+  when `-l` flag is set. This flag can be used to get more details about a
+  service.
+
+  ```sh
+  $ grpc_cli ls localhost:50051 helloworld.Greeter -l
+  ```
+
+  output:
+  ```sh
+  filename: helloworld.proto
+  package: helloworld;
+  service Greeter {
+    rpc SayHello(helloworld.HelloRequest) returns (helloworld.HelloReply) {}
+  }
+
+  ```
+
+### List methods
+
+- List one method with details
+
+  `grpc_cli ls` command also inspects a method given its full name (in the
+  format of \<package\>.\<service\>.\<method\>).
+
+  ```sh
+  $ grpc_cli ls localhost:50051 helloworld.Greeter.SayHello -l
+  ```
+
+  output:
+  ```sh
+    rpc SayHello(helloworld.HelloRequest) returns (helloworld.HelloReply) {}
+  ```
+
+### Inspect message types
+
+We can use`grpc_cli type` command to inspect request/response types given the
+full name of the type (in the format of \<package\>.\<type\>).
+
+- Get information about the request type
+
+  ```sh
+  $ grpc_cli type localhost:50051 helloworld.HelloRequest
+  ```
+
+  output:
+  ```sh
+  message HelloRequest {
+    optional string name = 1;
+  }
+  ```
+
+### Call a remote method
+
+We can send RPCs to a server and get responses using `grpc_cli call` command.
+
+- Call a unary method
+
+  ```sh
+  $ grpc_cli call localhost:50051 SayHello "name: 'gRPC CLI'"
+  ```
+
+  output:
+  ```sh
+  message: "Hello gRPC CLI"
+  ```
+
+## Use Server Reflection in a C++ client
+
+Server Reflection can be used by clients to get information about gRPC services
+at runtime. We've provided a descriptor database called
+[grpc::ProtoReflectionDescriptorDatabase](../test/cpp/util/proto_reflection_descriptor_database.h)
+which implements the
+[google::protobuf::DescriptorDatabase](https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.descriptor_database#DescriptorDatabase)
+interface. It manages the communication between clients and reflection services
+and the storage of received information. Clients can use it as using a local
+descriptor database.
+
+- To use Server Reflection with grpc::ProtoReflectionDescriptorDatabase, first
+  initialize an instance with a grpc::Channel.
+
+  ```c++
+  std::shared_ptr<grpc::Channel> channel =
+      grpc::CreateChannel(server_address, server_cred);
+  grpc::ProtoReflectionDescriptorDatabase reflection_db(channel);
+  ```
+
+- Then use this instance to feed a
+  [google::protobuf::DescriptorPool](https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.descriptor#DescriptorPool).
+
+  ```c++
+  google::protobuf::DescriptorPool desc_pool(&reflection_db);
+  ```
+
+- Example usage of this descriptor pool
+
+  * Get Service/method descriptors.
+
+    ```c++
+    const google::protobuf::ServiceDescriptor* service_desc =
+        desc_pool->FindServiceByName("helloworld.Greeter");
+    const google::protobuf::MethodDescriptor* method_desc =
+        desc_pool->FindMethodByName("helloworld.Greeter.SayHello");
+    ```
+
+  * Get message type descriptors.
+
+    ```c++
+    const google::protobuf::Descriptor* request_desc =
+        desc_pool->FindMessageTypeByName("helloworld.HelloRequest");
+    ```
+
+  * Feed [google::protobuf::DynamicMessageFactory](https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.dynamic_message#DynamicMessageFactory).
+
+    ```c++
+    google::protobuf::DynamicMessageFactory(&desc_pool);
+    ```
diff --git a/doc/wait-for-ready.md b/doc/wait-for-ready.md
new file mode 100644
index 0000000..fd42604
--- /dev/null
+++ b/doc/wait-for-ready.md
@@ -0,0 +1,14 @@
+gRPC Wait for Ready Semantics
+=============================
+
+If an RPC is issued but the channel is in `TRANSIENT_FAILURE` or `SHUTDOWN`
+states, the RPC is unable to be transmited promptly. By default, gRPC
+implementations SHOULD fail such RPCs immediately. This is known as "fail fast,"
+but usage of the term is historical. RPCs SHOULD NOT fail as a result of the
+channel being in other states (`CONNECTING`, `READY`, or `IDLE`).
+
+gRPC implementations MAY provide a per-RPC option to not fail RPCs as a result
+of the channel being in `TRANSIENT_FAILURE` state. Instead, the implementation
+queues the RPCs until the channel is `READY`. This is known as "wait for ready."
+The RPCs SHOULD still fail before `READY` if there are unrelated reasons, such
+as the channel is `SHUTDOWN` or the RPC's deadline is reached.
diff --git a/etc/README.md b/etc/README.md
new file mode 100644
index 0000000..87af44f
--- /dev/null
+++ b/etc/README.md
@@ -0,0 +1,4 @@
+The roots.pem file is periodically generated from:
+https://hg.mozilla.org/mozilla-central/raw-file/tip/security/nss/lib/ckfw/builtins/certdata.txt
+using
+https://github.com/agl/extract-nss-root-certs
diff --git a/etc/roots.pem b/etc/roots.pem
index e6df88e..d376e58 100644
--- a/etc/roots.pem
+++ b/etc/roots.pem
@@ -5387,3 +5387,42 @@
 j9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta3U1fJAuwACEl74+nBCZx
 4nxp5V2a+EEfOzmTk51V6s2N8fvB
 -----END CERTIFICATE-----
+
+# Issuer: CN=ISRG Root X1 O=Internet Security Research Group
+# Subject: CN=ISRG Root X1 O=Internet Security Research Group
+# Label: "ISRG Root X1"
+# Serial: 172886928669790476064670243504169061120
+# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e
+# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8
+# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
+TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
+cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
+WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
+ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
+MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
+h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
+A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
+T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
+B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
+B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
+KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
+OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
+jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
+qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
+rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
+hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
+ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
+3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
+NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
+ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
+TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
+jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
+oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
+4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
+mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
+emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
+-----END CERTIFICATE-----
diff --git a/examples/cpp/README.md b/examples/cpp/README.md
index 3fa7ad4..783935c 100644
--- a/examples/cpp/README.md
+++ b/examples/cpp/README.md
@@ -2,26 +2,14 @@
 
 ## Installation
 
-To install gRPC on your system, follow the instructions to build from source [here](../../INSTALL.md). This also installs the protocol buffer compiler `protoc` (if you don't have it already), and the C++ gRPC plugin for `protoc`.
+To install gRPC on your system, follow the instructions to build from source
+[here](../../INSTALL.md). This also installs the protocol buffer compiler
+`protoc` (if you don't have it already), and the C++ gRPC plugin for `protoc`.
 
 ## Hello C++ gRPC!
 
-Here's how to build and run the C++ implementation of the [Hello World](../protos/helloworld.proto) example used in [Getting started](..).
-
-The example code for this and our other examples lives in the `examples`
-directory. Clone this repository to your local machine by running the
-following command:
-
-
-```sh
-$ git clone -b $(curl -L http://grpc.io/release) https://github.com/grpc/grpc
-```
-
-Change your current directory to examples/cpp/helloworld
-
-```sh
-$ cd examples/cpp/helloworld/
-```
+Here's how to build and run the C++ implementation of the [Hello
+World](../protos/helloworld.proto) example used in [Getting started](..).
 
 ### Client and server implementations
 
@@ -31,18 +19,25 @@
 
 ### Try it!
 Build client and server:
+
 ```sh
 $ make
 ```
+
 Run the server, which will listen on port 50051:
+
 ```sh
 $ ./greeter_server
 ```
+
 Run the client (in a different terminal):
+
 ```sh
 $ ./greeter_client
 ```
-If things go smoothly, you will see the "Greeter received: Hello world" in the client side output.
+
+If things go smoothly, you will see the "Greeter received: Hello world" in the
+client side output.
 
 ## Tutorial
 
diff --git a/examples/cpp/cpptutorial.md b/examples/cpp/cpptutorial.md
index 80fef07..ae84aba 100644
--- a/examples/cpp/cpptutorial.md
+++ b/examples/cpp/cpptutorial.md
@@ -1,58 +1,77 @@
 #gRPC Basics: C++
 
-This tutorial provides a basic C++ programmer's introduction to working with gRPC. By walking through this example you'll learn how to:
+This tutorial provides a basic C++ programmer's introduction to working with
+gRPC. By walking through this example you'll learn how to:
 
-- Define a service in a .proto file.
+- Define a service in a `.proto` file.
 - Generate server and client code using the protocol buffer compiler.
 - Use the C++ gRPC API to write a simple client and server for your service.
 
-It assumes that you have read the [Getting started](..) guide and are familiar with [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). Note that the example in this tutorial uses the proto3 version of the protocol buffers language, which is currently in alpha release: you can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3) and see the [release notes](https://github.com/google/protobuf/releases) for the new version in the protocol buffers Github repository.
-
-This isn't a comprehensive guide to using gRPC in C++: more reference documentation is coming soon.
+It assumes that you are familiar with
+[protocol buffers](https://developers.google.com/protocol-buffers/docs/overview).
+Note that the example in this tutorial uses the proto3 version of the protocol
+buffers language, which is currently in alpha release: you can find out more in
+the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3)
+and see the [release notes](https://github.com/google/protobuf/releases) for the
+new version in the protocol buffers Github repository.
 
 ## Why use gRPC?
 
-Our example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients.
+Our example is a simple route mapping application that lets clients get
+information about features on their route, create a summary of their route, and
+exchange route information such as traffic updates with the server and other
+clients.
 
-With gRPC we can define our service once in a .proto file and implement clients and servers in any of gRPC's supported languages, which in turn can be run in environments ranging from servers inside Google to your own tablet - all the complexity of communication between different languages and environments is handled for you by gRPC. We also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating.
+With gRPC we can define our service once in a `.proto` file and implement clients
+and servers in any of gRPC's supported languages, which in turn can be run in
+environments ranging from servers inside Google to your own tablet - all the
+complexity of communication between different languages and environments is
+handled for you by gRPC. We also get all the advantages of working with protocol
+buffers, including efficient serialization, a simple IDL, and easy interface
+updating.
 
 ## Example code and setup
 
-The example code for our tutorial is in [examples/cpp/route_guide](route_guide). To download the example, clone this repository by running the following command:
-```shell
-$ git clone -b $(curl -L http://grpc.io/release) https://github.com/grpc/grpc
-```
-
-Then change your current directory to `examples/cpp/route_guide`:
-```shell
-$ cd examples/cpp/route_guide
-```
-
-You also should have the relevant tools installed to generate the server and client interface code - if you don't already, follow the setup instructions in [gRPC in 3 minutes](README.md).
-
+The example code for our tutorial is in [examples/cpp/route_guide](route_guide).
+You also should have the relevant tools installed to generate the server and
+client interface code - if you don't already, follow the setup instructions in
+[INSTALL.md](../../INSTALL.md).
 
 ## Defining the service
 
-Our first step (as you'll know from [Getting started](..) is to define the gRPC *service* and the method *request* and *response* types using [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). You can see the complete .proto file in [`examples/protos/route_guide.proto`](../protos/route_guide.proto).
+Our first step is to define the gRPC *service* and the method *request* and
+*response* types using
+[protocol buffers](https://developers.google.com/protocol-buffers/docs/overview).
+You can see the complete `.proto` file in
+[`examples/protos/route_guide.proto`](../protos/route_guide.proto).
 
-To define a service, you specify a named `service` in your .proto file:
+To define a service, you specify a named `service` in your `.proto` file:
 
-```
+```protobuf
 service RouteGuide {
    ...
 }
 ```
 
-Then you define `rpc` methods inside your service definition, specifying their request and response types. gRPC lets you define four kinds of service method, all of which are used in the `RouteGuide` service:
+Then you define `rpc` methods inside your service definition, specifying their
+request and response types. gRPC lets you define four kinds of service method,
+all of which are used in the `RouteGuide` service:
 
-- A *simple RPC* where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call.
-```
+- A *simple RPC* where the client sends a request to the server using the stub
+  and waits for a response to come back, just like a normal function call.
+
+```protobuf
    // Obtains the feature at a given position.
    rpc GetFeature(Point) returns (Feature) {}
 ```
 
-- A *server-side streaming RPC* where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages. As you can see in our example, you specify a server-side streaming method by placing the `stream` keyword before the *response* type.
-```
+- A *server-side streaming RPC* where the client sends a request to the server
+  and gets a stream to read a sequence of messages back. The client reads from
+  the returned stream until there are no more messages. As you can see in our
+  example, you specify a server-side streaming method by placing the `stream`
+  keyword before the *response* type.
+
+```protobuf
   // Obtains the Features available within the given Rectangle.  Results are
   // streamed rather than returned at once (e.g. in a response message with a
   // repeated field), as the rectangle may cover a large area and contain a
@@ -60,22 +79,38 @@
   rpc ListFeatures(Rectangle) returns (stream Feature) {}
 ```
 
-- A *client-side streaming RPC* where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a client-side streaming method by placing the `stream` keyword before the *request* type.
-```
+- A *client-side streaming RPC* where the client writes a sequence of messages
+  and sends them to the server, again using a provided stream. Once the client
+  has finished writing the messages, it waits for the server to read them all
+  and return its response. You specify a client-side streaming method by placing
+  the `stream` keyword before the *request* type.
+
+```protobuf
   // Accepts a stream of Points on a route being traversed, returning a
   // RouteSummary when traversal is completed.
   rpc RecordRoute(stream Point) returns (RouteSummary) {}
 ```
 
-- A *bidirectional streaming RPC* where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the `stream` keyword before both the request and the response.
-```
+- A *bidirectional streaming RPC* where both sides send a sequence of messages
+  using a read-write stream. The two streams operate independently, so clients
+  and servers can read and write in whatever order they like: for example, the
+  server could wait to receive all the client messages before writing its
+  responses, or it could alternately read a message then write a message, or
+  some other combination of reads and writes. The order of messages in each
+  stream is preserved. You specify this type of method by placing the `stream`
+  keyword before both the request and the response.
+
+```protobuf
   // Accepts a stream of RouteNotes sent while a route is being traversed,
   // while receiving other RouteNotes (e.g. from other users).
   rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
 ```
 
-Our .proto file also contains protocol buffer message type definitions for all the request and response types used in our service methods - for example, here's the `Point` message type:
-```
+Our `.proto` file also contains protocol buffer message type definitions for all
+the request and response types used in our service methods - for example, here's
+the `Point` message type:
+
+```protobuf
 // Points are represented as latitude-longitude pairs in the E7 representation
 // (degrees multiplied by 10**7 and rounded to the nearest integer).
 // Latitudes should be in the range +/- 90 degrees and longitude should be in
@@ -86,12 +121,16 @@
 }
 ```
 
-
 ## Generating client and server code
 
-Next we need to generate the gRPC client and server interfaces from our .proto service definition. We do this using the protocol buffer compiler `protoc` with a special gRPC C++ plugin.
+Next we need to generate the gRPC client and server interfaces from our `.proto`
+service definition. We do this using the protocol buffer compiler `protoc` with
+a special gRPC C++ plugin.
 
-For simplicity, we've provided a [makefile](route_guide/Makefile) that runs `protoc` for you with the appropriate plugin, input, and output (if you want to run this yourself, make sure you've installed protoc and followed the gRPC code [installation instructions](../../INSTALL.md) first):
+For simplicity, we've provided a [Makefile](route_guide/Makefile) that runs
+`protoc` for you with the appropriate plugin, input, and output (if you want to
+run this yourself, make sure you've installed protoc and followed the gRPC code
+[installation instructions](../../INSTALL.md) first):
 
 ```shell
 $ make route_guide.grpc.pb.cc route_guide.pb.cc
@@ -107,39 +146,58 @@
 Running this command generates the following files in your current directory:
 - `route_guide.pb.h`, the header which declares your generated message classes
 - `route_guide.pb.cc`, which contains the implementation of your message classes
-- `route_guide.grpc.pb.h`, the header which declares your generated service classes
-- `route_guide.grpc.pb.cc`, which contains the implementation of your service classes
+- `route_guide.grpc.pb.h`, the header which declares your generated service
+  classes
+- `route_guide.grpc.pb.cc`, which contains the implementation of your service
+  classes
 
 These contain:
-- All the protocol buffer code to populate, serialize, and retrieve our request and response message types
+- All the protocol buffer code to populate, serialize, and retrieve our request
+  and response message types
 - A class called `RouteGuide` that contains
-   - a remote interface type (or *stub*) for clients to call with the methods defined in the `RouteGuide` service.
-   - two abstract interfaces for servers to implement, also with the methods defined in the `RouteGuide` service.
+   - a remote interface type (or *stub*) for clients to call with the methods
+     defined in the `RouteGuide` service.
+   - two abstract interfaces for servers to implement, also with the methods
+     defined in the `RouteGuide` service.
 
 
 <a name="server"></a>
 ## Creating the server
 
-First let's look at how we create a `RouteGuide` server. If you're only interested in creating gRPC clients, you can skip this section and go straight to [Creating the client](#client) (though you might find it interesting anyway!).
+First let's look at how we create a `RouteGuide` server. If you're only
+interested in creating gRPC clients, you can skip this section and go straight
+to [Creating the client](#client) (though you might find it interesting
+anyway!).
 
 There are two parts to making our `RouteGuide` service do its job:
-- Implementing the service interface generated from our service definition: doing the actual "work" of our service.
-- Running a gRPC server to listen for requests from clients and return the service responses.
+- Implementing the service interface generated from our service definition:
+  doing the actual "work" of our service.
+- Running a gRPC server to listen for requests from clients and return the
+  service responses.
 
-You can find our example `RouteGuide` server in [route_guide/route_guide_server.cc](route_guide/route_guide_server.cc). Let's take a closer look at how it works.
+You can find our example `RouteGuide` server in
+[route_guide/route_guide_server.cc](route_guide/route_guide_server.cc). Let's
+take a closer look at how it works.
 
 ### Implementing RouteGuide
 
-As you can see, our server has a `RouteGuideImpl` class that implements the generated `RouteGuide::Service` interface:
+As you can see, our server has a `RouteGuideImpl` class that implements the
+generated `RouteGuide::Service` interface:
 
 ```cpp
 class RouteGuideImpl final : public RouteGuide::Service {
 ...
 }
 ```
-In this case we're implementing the *synchronous* version of `RouteGuide`, which provides our default gRPC server behaviour. It's also possible to implement an asynchronous interface, `RouteGuide::AsyncService`, which allows you to further customize your server's threading behaviour, though we won't look at this in this tutorial.
+In this case we're implementing the *synchronous* version of `RouteGuide`, which
+provides our default gRPC server behaviour. It's also possible to implement an
+asynchronous interface, `RouteGuide::AsyncService`, which allows you to further
+customize your server's threading behaviour, though we won't look at this in
+this tutorial.
 
-`RouteGuideImpl` implements all our service methods. Let's look at the simplest type first, `GetFeature`, which just gets a `Point` from the client and returns the corresponding feature information from its database in a `Feature`.
+`RouteGuideImpl` implements all our service methods. Let's look at the simplest
+type first, `GetFeature`, which just gets a `Point` from the client and returns
+the corresponding feature information from its database in a `Feature`.
 
 ```cpp
   Status GetFeature(ServerContext* context, const Point* point,
@@ -150,34 +208,52 @@
   }
 ```
 
-The method is passed a context object for the RPC, the client's `Point` protocol buffer request, and a `Feature` protocol buffer to fill in with the response information. In the method we populate the `Feature` with the appropriate information, and then `return` with an `OK` status to tell gRPC that we've finished dealing with the RPC and that the `Feature` can be returned to the client.
+The method is passed a context object for the RPC, the client's `Point` protocol
+buffer request, and a `Feature` protocol buffer to fill in with the response
+information. In the method we populate the `Feature` with the appropriate
+information, and then `return` with an `OK` status to tell gRPC that we've
+finished dealing with the RPC and that the `Feature` can be returned to the
+client.
 
-Now let's look at something a bit more complicated - a streaming RPC. `ListFeatures` is a server-side streaming RPC, so we need to send back multiple `Feature`s to our client.
+Now let's look at something a bit more complicated - a streaming RPC.
+`ListFeatures` is a server-side streaming RPC, so we need to send back multiple
+`Feature`s to our client.
 
 ```cpp
-  Status ListFeatures(ServerContext* context, const Rectangle* rectangle,
-                      ServerWriter<Feature>* writer) override {
-    auto lo = rectangle->lo();
-    auto hi = rectangle->hi();
-    long left = std::min(lo.longitude(), hi.longitude());
-    long right = std::max(lo.longitude(), hi.longitude());
-    long top = std::max(lo.latitude(), hi.latitude());
-    long bottom = std::min(lo.latitude(), hi.latitude());
-    for (const Feature& f : feature_list_) {
-      if (f.location().longitude() >= left &&
-          f.location().longitude() <= right &&
-          f.location().latitude() >= bottom &&
-          f.location().latitude() <= top) {
-        writer->Write(f);
-      }
+Status ListFeatures(ServerContext* context, const Rectangle* rectangle,
+                    ServerWriter<Feature>* writer) override {
+  auto lo = rectangle->lo();
+  auto hi = rectangle->hi();
+  long left = std::min(lo.longitude(), hi.longitude());
+  long right = std::max(lo.longitude(), hi.longitude());
+  long top = std::max(lo.latitude(), hi.latitude());
+  long bottom = std::min(lo.latitude(), hi.latitude());
+  for (const Feature& f : feature_list_) {
+    if (f.location().longitude() >= left &&
+        f.location().longitude() <= right &&
+        f.location().latitude() >= bottom &&
+        f.location().latitude() <= top) {
+      writer->Write(f);
     }
-    return Status::OK;
   }
+  return Status::OK;
+}
 ```
 
-As you can see, instead of getting simple request and response objects in our method parameters, this time we get a request object (the `Rectangle` in which our client wants to find `Feature`s) and a special `ServerWriter` object. In the method, we populate as many `Feature` objects as we need to return, writing them to the `ServerWriter` using its `Write()` method. Finally, as in our simple RPC, we `return Status::OK` to tell gRPC that we've finished writing responses.
+As you can see, instead of getting simple request and response objects in our
+method parameters, this time we get a request object (the `Rectangle` in which
+our client wants to find `Feature`s) and a special `ServerWriter` object. In the
+method, we populate as many `Feature` objects as we need to return, writing them
+to the `ServerWriter` using its `Write()` method. Finally, as in our simple RPC,
+we `return Status::OK` to tell gRPC that we've finished writing responses.
 
-If you look at the client-side streaming method `RecordRoute` you'll see it's quite similar, except this time we get a `ServerReader` instead of a request object and a single response. We use the `ServerReader`s `Read()` method to repeatedly read in our client's requests to a request object (in this case a `Point`) until there are no more messages: the server needs to check the return value of `Read()` after each call. If `true`, the stream is still good and it can continue reading; if `false` the message stream has ended.
+If you look at the client-side streaming method `RecordRoute` you'll see it's
+quite similar, except this time we get a `ServerReader` instead of a request
+object and a single response. We use the `ServerReader`s `Read()` method to
+repeatedly read in our client's requests to a request object (in this case a
+`Point`) until there are no more messages: the server needs to check the return
+value of `Read()` after each call. If `true`, the stream is still good and it
+can continue reading; if `false` the message stream has ended.
 
 ```cpp
 while (stream->Read(&point)) {
@@ -205,11 +281,18 @@
   }
 ```
 
-This time we get a `ServerReaderWriter` that can be used to read *and* write messages. The syntax for reading and writing here is exactly the same as for our client-streaming and server-streaming methods. Although each side will always get the other's messages in the order they were written, both the client and server can read and write in any order — the streams operate completely independently.
+This time we get a `ServerReaderWriter` that can be used to read *and* write
+messages. The syntax for reading and writing here is exactly the same as for our
+client-streaming and server-streaming methods. Although each side will always
+get the other's messages in the order they were written, both the client and
+server can read and write in any order — the streams operate completely
+independently.
 
 ### Starting the server
 
-Once we've implemented all our methods, we also need to start up a gRPC server so that clients can actually use our service. The following snippet shows how we do this for our `RouteGuide` service:
+Once we've implemented all our methods, we also need to start up a gRPC server
+so that clients can actually use our service. The following snippet shows how we
+do this for our `RouteGuide` service:
 
 ```cpp
 void RunServer(const std::string& db_path) {
@@ -227,44 +310,55 @@
 As you can see, we build and start our server using a `ServerBuilder`. To do this, we:
 
 1. Create an instance of our service implementation class `RouteGuideImpl`.
-2. Create an instance of the factory `ServerBuilder` class.
-3. Specify the address and port we want to use to listen for client requests using the builder's `AddListeningPort()` method.
-4. Register our service implementation with the builder.
-5. Call `BuildAndStart()` on the builder to create and start an RPC server for our service.
-5. Call `Wait()` on the server to do a blocking wait until process is killed or `Shutdown()` is called.
+1. Create an instance of the factory `ServerBuilder` class.
+1. Specify the address and port we want to use to listen for client requests
+   using the builder's `AddListeningPort()` method.
+1. Register our service implementation with the builder.
+1. Call `BuildAndStart()` on the builder to create and start an RPC server for
+   our service.
+1. Call `Wait()` on the server to do a blocking wait until process is killed or
+   `Shutdown()` is called.
 
 <a name="client"></a>
 ## Creating the client
 
-In this section, we'll look at creating a C++ client for our `RouteGuide` service. You can see our complete example client code in [route_guide/route_guide_client.cc](route_guide/route_guide_client.cc).
+In this section, we'll look at creating a C++ client for our `RouteGuide`
+service. You can see our complete example client code in
+[route_guide/route_guide_client.cc](route_guide/route_guide_client.cc).
 
 ### Creating a stub
 
 To call service methods, we first need to create a *stub*.
 
-First we need to create a gRPC *channel* for our stub, specifying the server address and port we want to connect to without SSL:
+First we need to create a gRPC *channel* for our stub, specifying the server
+address and port we want to connect to without SSL:
 
 ```cpp
 grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials());
 ```
 
-Now we can use the channel to create our stub using the `NewStub` method provided in the `RouteGuide` class we generated from our .proto.
+Now we can use the channel to create our stub using the `NewStub` method
+provided in the `RouteGuide` class we generated from our `.proto`.
 
 ```cpp
- public:
-  RouteGuideClient(std::shared_ptr<Channel> channel, const std::string& db)
-      : stub_(RouteGuide::NewStub(channel)) {
-    ...
-  }
+public:
+ RouteGuideClient(std::shared_ptr<Channel> channel, const std::string& db)
+     : stub_(RouteGuide::NewStub(channel)) {
+   ...
+ }
 ```
 
 ### Calling service methods
 
-Now let's look at how we call our service methods. Note that in this tutorial we're calling the *blocking/synchronous* versions of each method: this means that the RPC call waits for the server to respond, and will either return a response or raise an exception.
+Now let's look at how we call our service methods. Note that in this tutorial
+we're calling the *blocking/synchronous* versions of each method: this means
+that the RPC call waits for the server to respond, and will either return a
+response or raise an exception.
 
 #### Simple RPC
 
-Calling the simple RPC `GetFeature` is nearly as straightforward as calling a local method.
+Calling the simple RPC `GetFeature` is nearly as straightforward as calling a
+local method.
 
 ```cpp
   Point point;
@@ -281,33 +375,53 @@
   }
 ```
 
-As you can see, we create and populate a request protocol buffer object (in our case `Point`), and create a response protocol buffer object for the server to fill in. We also create a `ClientContext` object for our call - you can optionally set RPC configuration values on this object, such as deadlines, though for now we'll use the default settings. Note that you cannot reuse this object between calls. Finally, we call the method on the stub, passing it the context, request, and response. If the method returns `OK`, then we can read the response information from the server from our response object.
+As you can see, we create and populate a request protocol buffer object (in our
+case `Point`), and create a response protocol buffer object for the server to
+fill in. We also create a `ClientContext` object for our call - you can
+optionally set RPC configuration values on this object, such as deadlines,
+though for now we'll use the default settings. Note that you cannot reuse this
+object between calls. Finally, we call the method on the stub, passing it the
+context, request, and response. If the method returns `OK`, then we can read the
+response information from the server from our response object.
 
 ```cpp
-      std::cout << "Found feature called " << feature->name()  << " at "
-                << feature->location().latitude()/kCoordFactor_ << ", "
-                << feature->location().longitude()/kCoordFactor_ << std::endl;
+std::cout << "Found feature called " << feature->name()  << " at "
+          << feature->location().latitude()/kCoordFactor_ << ", "
+          << feature->location().longitude()/kCoordFactor_ << std::endl;
 ```
 
 #### Streaming RPCs
 
-Now let's look at our streaming methods. If you've already read [Creating the server](#server) some of this may look very familiar - streaming RPCs are implemented in a similar way on both sides. Here's where we call the server-side streaming method `ListFeatures`, which returns a stream of geographical `Feature`s:
+Now let's look at our streaming methods. If you've already read [Creating the
+server](#server) some of this may look very familiar - streaming RPCs are
+implemented in a similar way on both sides. Here's where we call the server-side
+streaming method `ListFeatures`, which returns a stream of geographical
+`Feature`s:
 
 ```cpp
-    std::unique_ptr<ClientReader<Feature> > reader(
-        stub_->ListFeatures(&context, rect));
-    while (reader->Read(&feature)) {
-      std::cout << "Found feature called "
-                << feature.name() << " at "
-                << feature.location().latitude()/kCoordFactor_ << ", "
-                << feature.location().longitude()/kCoordFactor_ << std::endl;
-    }
-    Status status = reader->Finish();
+std::unique_ptr<ClientReader<Feature> > reader(
+    stub_->ListFeatures(&context, rect));
+while (reader->Read(&feature)) {
+  std::cout << "Found feature called "
+            << feature.name() << " at "
+            << feature.location().latitude()/kCoordFactor_ << ", "
+            << feature.location().longitude()/kCoordFactor_ << std::endl;
+}
+Status status = reader->Finish();
 ```
 
-Instead of passing the method a context, request, and response, we pass it a context and request and get a `ClientReader` object back. The client can use the `ClientReader` to read the server's responses. We use the `ClientReader`s `Read()` method to repeatedly read in the server's responses to a response protocol buffer object (in this case a `Feature`) until there are no more messages: the client needs to check the return value of `Read()` after each call. If `true`, the stream is still good and it can continue reading; if `false` the message stream has ended. Finally, we call `Finish()` on the stream to complete the call and get our RPC status.
+Instead of passing the method a context, request, and response, we pass it a
+context and request and get a `ClientReader` object back. The client can use the
+`ClientReader` to read the server's responses. We use the `ClientReader`s
+`Read()` method to repeatedly read in the server's responses to a response
+protocol buffer object (in this case a `Feature`) until there are no more
+messages: the client needs to check the return value of `Read()` after each
+call. If `true`, the stream is still good and it can continue reading; if
+`false` the message stream has ended. Finally, we call `Finish()` on the stream
+to complete the call and get our RPC status.
 
-The client-side streaming method `RecordRoute` is similar, except there we pass the method a context and response object and get back a `ClientWriter`.
+The client-side streaming method `RecordRoute` is similar, except there we pass
+the method a context and response object and get back a `ClientWriter`.
 
 ```cpp
     std::unique_ptr<ClientWriter<Point> > writer(
@@ -337,16 +451,26 @@
     }
 ```
 
-Once we've finished writing our client's requests to the stream using `Write()`, we need to call `WritesDone()` on the stream to let gRPC know that we've finished writing, then `Finish()` to complete the call and get our RPC status. If the status is `OK`, our response object that we initially passed to `RecordRoute()` will be populated with the server's response.
+Once we've finished writing our client's requests to the stream using `Write()`,
+we need to call `WritesDone()` on the stream to let gRPC know that we've
+finished writing, then `Finish()` to complete the call and get our RPC status.
+If the status is `OK`, our response object that we initially passed to
+`RecordRoute()` will be populated with the server's response.
 
-Finally, let's look at our bidirectional streaming RPC `RouteChat()`. In this case, we just pass a context to the method and get back a `ClientReaderWriter`, which we can use to both write and read messages.
+Finally, let's look at our bidirectional streaming RPC `RouteChat()`. In this
+case, we just pass a context to the method and get back a `ClientReaderWriter`,
+which we can use to both write and read messages.
 
 ```cpp
-    std::shared_ptr<ClientReaderWriter<RouteNote, RouteNote> > stream(
-        stub_->RouteChat(&context));
+std::shared_ptr<ClientReaderWriter<RouteNote, RouteNote> > stream(
+    stub_->RouteChat(&context));
 ```
 
-The syntax for reading and writing here is exactly the same as for our client-streaming and server-streaming methods. Although each side will always get the other's messages in the order they were written, both the client and server can read and write in any order — the streams operate completely independently.
+The syntax for reading and writing here is exactly the same as for our
+client-streaming and server-streaming methods. Although each side will always
+get the other's messages in the order they were written, both the client and
+server can read and write in any order — the streams operate completely
+independently.
 
 ## Try it out!
 
@@ -362,4 +486,3 @@
 ```shell
 $ ./route_guide_client
 ```
-
diff --git a/examples/cpp/helloworld/Makefile b/examples/cpp/helloworld/Makefile
index df82f46..2c50b82 100644
--- a/examples/cpp/helloworld/Makefile
+++ b/examples/cpp/helloworld/Makefile
@@ -107,7 +107,7 @@
 	@echo "Please install Google protocol buffers 3.0.0 and its compiler."
 	@echo "You can find it here:"
 	@echo
-	@echo "   https://github.com/google/protobuf/releases/tag/v3.0.0-beta-3.3"
+	@echo "   https://github.com/google/protobuf/releases/tag/v3.0.0"
 	@echo
 	@echo "Here is what I get when trying to evaluate your version of protoc:"
 	@echo
diff --git a/examples/cpp/helloworld/greeter_async_client.cc b/examples/cpp/helloworld/greeter_async_client.cc
index 33de59f..595baae 100644
--- a/examples/cpp/helloworld/greeter_async_client.cc
+++ b/examples/cpp/helloworld/greeter_async_client.cc
@@ -36,6 +36,7 @@
 #include <string>
 
 #include <grpc++/grpc++.h>
+#include <grpc/support/log.h>
 
 #include "helloworld.grpc.pb.h"
 
diff --git a/examples/cpp/helloworld/greeter_async_client2.cc b/examples/cpp/helloworld/greeter_async_client2.cc
index 0902376..fe0a213 100644
--- a/examples/cpp/helloworld/greeter_async_client2.cc
+++ b/examples/cpp/helloworld/greeter_async_client2.cc
@@ -36,6 +36,7 @@
 #include <string>
 
 #include <grpc++/grpc++.h>
+#include <grpc/support/log.h>
 #include <thread>
 
 #include "helloworld.grpc.pb.h"
diff --git a/examples/cpp/helloworld/greeter_async_server.cc b/examples/cpp/helloworld/greeter_async_server.cc
index ead4418..bd14b0e 100644
--- a/examples/cpp/helloworld/greeter_async_server.cc
+++ b/examples/cpp/helloworld/greeter_async_server.cc
@@ -37,6 +37,7 @@
 #include <thread>
 
 #include <grpc++/grpc++.h>
+#include <grpc/support/log.h>
 
 #include "helloworld.grpc.pb.h"
 
diff --git a/examples/cpp/route_guide/Makefile b/examples/cpp/route_guide/Makefile
index ba5e45c..00cf9ad 100644
--- a/examples/cpp/route_guide/Makefile
+++ b/examples/cpp/route_guide/Makefile
@@ -96,7 +96,7 @@
 	@echo "Please install Google protocol buffers 3.0.0 and its compiler."
 	@echo "You can find it here:"
 	@echo
-	@echo "   https://github.com/google/protobuf/releases/tag/v3.0.0-beta-3.3"
+	@echo "   https://github.com/google/protobuf/releases/tag/v3.0.0"
 	@echo
 	@echo "Here is what I get when trying to evaluate your version of protoc:"
 	@echo
diff --git a/examples/csharp/.nuget/packages.config b/examples/csharp/.nuget/packages.config
deleted file mode 100644
index b143730..0000000
--- a/examples/csharp/.nuget/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="Grpc.Tools" version="0.6.0" />
-</packages>
\ No newline at end of file
diff --git a/examples/csharp/helloworld-from-cli/Greeter/Helloworld.cs b/examples/csharp/helloworld-from-cli/Greeter/Helloworld.cs
new file mode 100644
index 0000000..6477b4f
--- /dev/null
+++ b/examples/csharp/helloworld-from-cli/Greeter/Helloworld.cs
@@ -0,0 +1,259 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: helloworld.proto
+#pragma warning disable 1591, 0612, 3021
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Helloworld {
+
+  /// <summary>Holder for reflection information generated from helloworld.proto</summary>
+  [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+  public static partial class HelloworldReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for helloworld.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static HelloworldReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChBoZWxsb3dvcmxkLnByb3RvEgpoZWxsb3dvcmxkIhwKDEhlbGxvUmVxdWVz",
+            "dBIMCgRuYW1lGAEgASgJIh0KCkhlbGxvUmVwbHkSDwoHbWVzc2FnZRgBIAEo",
+            "CTJJCgdHcmVldGVyEj4KCFNheUhlbGxvEhguaGVsbG93b3JsZC5IZWxsb1Jl",
+            "cXVlc3QaFi5oZWxsb3dvcmxkLkhlbGxvUmVwbHkiAEI2Chtpby5ncnBjLmV4",
+            "YW1wbGVzLmhlbGxvd29ybGRCD0hlbGxvV29ybGRQcm90b1ABogIDSExXYgZw",
+            "cm90bzM="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { },
+          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Helloworld.HelloRequest), global::Helloworld.HelloRequest.Parser, new[]{ "Name" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Helloworld.HelloReply), global::Helloworld.HelloReply.Parser, new[]{ "Message" }, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  /// <summary>
+  ///  The request message containing the user's name.
+  /// </summary>
+  [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+  public sealed partial class HelloRequest : pb::IMessage<HelloRequest> {
+    private static readonly pb::MessageParser<HelloRequest> _parser = new pb::MessageParser<HelloRequest>(() => new HelloRequest());
+    public static pb::MessageParser<HelloRequest> Parser { get { return _parser; } }
+
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Helloworld.HelloworldReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    public HelloRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    public HelloRequest(HelloRequest other) : this() {
+      name_ = other.name_;
+    }
+
+    public HelloRequest Clone() {
+      return new HelloRequest(this);
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 1;
+    private string name_ = "";
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    public override bool Equals(object other) {
+      return Equals(other as HelloRequest);
+    }
+
+    public bool Equals(HelloRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Name != other.Name) return false;
+      return true;
+    }
+
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      return hash;
+    }
+
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+    }
+
+    public int CalculateSize() {
+      int size = 0;
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      return size;
+    }
+
+    public void MergeFrom(HelloRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+    }
+
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 10: {
+            Name = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  ///  The response message containing the greetings
+  /// </summary>
+  [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+  public sealed partial class HelloReply : pb::IMessage<HelloReply> {
+    private static readonly pb::MessageParser<HelloReply> _parser = new pb::MessageParser<HelloReply>(() => new HelloReply());
+    public static pb::MessageParser<HelloReply> Parser { get { return _parser; } }
+
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Helloworld.HelloworldReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    public HelloReply() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    public HelloReply(HelloReply other) : this() {
+      message_ = other.message_;
+    }
+
+    public HelloReply Clone() {
+      return new HelloReply(this);
+    }
+
+    /// <summary>Field number for the "message" field.</summary>
+    public const int MessageFieldNumber = 1;
+    private string message_ = "";
+    public string Message {
+      get { return message_; }
+      set {
+        message_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    public override bool Equals(object other) {
+      return Equals(other as HelloReply);
+    }
+
+    public bool Equals(HelloReply other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Message != other.Message) return false;
+      return true;
+    }
+
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Message.Length != 0) hash ^= Message.GetHashCode();
+      return hash;
+    }
+
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (Message.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Message);
+      }
+    }
+
+    public int CalculateSize() {
+      int size = 0;
+      if (Message.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Message);
+      }
+      return size;
+    }
+
+    public void MergeFrom(HelloReply other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Message.Length != 0) {
+        Message = other.Message;
+      }
+    }
+
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 10: {
+            Message = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/examples/csharp/helloworld-from-cli/Greeter/HelloworldGrpc.cs b/examples/csharp/helloworld-from-cli/Greeter/HelloworldGrpc.cs
new file mode 100644
index 0000000..041f5a7
--- /dev/null
+++ b/examples/csharp/helloworld-from-cli/Greeter/HelloworldGrpc.cs
@@ -0,0 +1,143 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: helloworld.proto
+// Original file comments:
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+#region Designer generated code
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core;
+
+namespace Helloworld {
+  /// <summary>
+  ///  The greeting service definition.
+  /// </summary>
+  public static class Greeter
+  {
+    static readonly string __ServiceName = "helloworld.Greeter";
+
+    static readonly Marshaller<global::Helloworld.HelloRequest> __Marshaller_HelloRequest = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Helloworld.HelloRequest.Parser.ParseFrom);
+    static readonly Marshaller<global::Helloworld.HelloReply> __Marshaller_HelloReply = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Helloworld.HelloReply.Parser.ParseFrom);
+
+    static readonly Method<global::Helloworld.HelloRequest, global::Helloworld.HelloReply> __Method_SayHello = new Method<global::Helloworld.HelloRequest, global::Helloworld.HelloReply>(
+        MethodType.Unary,
+        __ServiceName,
+        "SayHello",
+        __Marshaller_HelloRequest,
+        __Marshaller_HelloReply);
+
+    /// <summary>Service descriptor</summary>
+    public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
+    {
+      get { return global::Helloworld.HelloworldReflection.Descriptor.Services[0]; }
+    }
+
+    /// <summary>Base class for server-side implementations of Greeter</summary>
+    public abstract class GreeterBase
+    {
+      /// <summary>
+      ///  Sends a greeting
+      /// </summary>
+      public virtual global::System.Threading.Tasks.Task<global::Helloworld.HelloReply> SayHello(global::Helloworld.HelloRequest request, ServerCallContext context)
+      {
+        throw new RpcException(new Status(StatusCode.Unimplemented, ""));
+      }
+
+    }
+
+    /// <summary>Client for Greeter</summary>
+    public class GreeterClient : ClientBase<GreeterClient>
+    {
+      /// <summary>Creates a new client for Greeter</summary>
+      /// <param name="channel">The channel to use to make remote calls.</param>
+      public GreeterClient(Channel channel) : base(channel)
+      {
+      }
+      /// <summary>Creates a new client for Greeter that uses a custom <c>CallInvoker</c>.</summary>
+      /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      public GreeterClient(CallInvoker callInvoker) : base(callInvoker)
+      {
+      }
+      /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      protected GreeterClient() : base()
+      {
+      }
+      /// <summary>Protected constructor to allow creation of configured clients.</summary>
+      /// <param name="configuration">The client configuration.</param>
+      protected GreeterClient(ClientBaseConfiguration configuration) : base(configuration)
+      {
+      }
+
+      /// <summary>
+      ///  Sends a greeting
+      /// </summary>
+      public virtual global::Helloworld.HelloReply SayHello(global::Helloworld.HelloRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      {
+        return SayHello(request, new CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      ///  Sends a greeting
+      /// </summary>
+      public virtual global::Helloworld.HelloReply SayHello(global::Helloworld.HelloRequest request, CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_SayHello, null, options, request);
+      }
+      /// <summary>
+      ///  Sends a greeting
+      /// </summary>
+      public virtual AsyncUnaryCall<global::Helloworld.HelloReply> SayHelloAsync(global::Helloworld.HelloRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      {
+        return SayHelloAsync(request, new CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      ///  Sends a greeting
+      /// </summary>
+      public virtual AsyncUnaryCall<global::Helloworld.HelloReply> SayHelloAsync(global::Helloworld.HelloRequest request, CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_SayHello, null, options, request);
+      }
+      protected override GreeterClient NewInstance(ClientBaseConfiguration configuration)
+      {
+        return new GreeterClient(configuration);
+      }
+    }
+
+    /// <summary>Creates service definition that can be registered with a server</summary>
+    public static ServerServiceDefinition BindService(GreeterBase serviceImpl)
+    {
+      return ServerServiceDefinition.CreateBuilder()
+          .AddMethod(__Method_SayHello, serviceImpl.SayHello).Build();
+    }
+
+  }
+}
+#endregion
diff --git a/examples/csharp/helloworld-from-cli/Greeter/project.json b/examples/csharp/helloworld-from-cli/Greeter/project.json
new file mode 100644
index 0000000..40fba7c
--- /dev/null
+++ b/examples/csharp/helloworld-from-cli/Greeter/project.json
@@ -0,0 +1,22 @@
+{
+  "title": "Greeter",
+  "version": "1.0.0-*",
+  "buildOptions": {
+    "debugType": "portable",
+  },
+  "dependencies": {
+    "Google.Protobuf": "3.0.0",
+    "Grpc": "1.0.0",
+  },
+  "frameworks": {
+    "net45": {
+      "frameworkAssemblies": {
+        "System.Runtime": "",
+        "System.IO": ""
+      },
+      "dependencies": {
+	"Microsoft.NETCore.Platforms": "1.0.1" 
+      }
+    }
+  }
+}
diff --git a/examples/csharp/helloworld-from-cli/GreeterClient/Program.cs b/examples/csharp/helloworld-from-cli/GreeterClient/Program.cs
new file mode 100644
index 0000000..444d473
--- /dev/null
+++ b/examples/csharp/helloworld-from-cli/GreeterClient/Program.cs
@@ -0,0 +1,53 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+using System;
+using Grpc.Core;
+using Helloworld;
+
+namespace GreeterClient
+{
+    class Program
+    {
+        public static void Main(string[] args)
+        {
+            Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
+
+            var client = new Greeter.GreeterClient(channel);
+            String user = "you";
+
+            var reply = client.SayHello(new HelloRequest { Name = user });
+            Console.WriteLine("Greeting: " + reply.Message);
+
+            channel.ShutdownAsync().Wait();
+            Console.WriteLine("Press any key to exit...");
+            Console.ReadKey();
+        }
+    }
+}
diff --git a/examples/csharp/helloworld-from-cli/GreeterClient/project.json b/examples/csharp/helloworld-from-cli/GreeterClient/project.json
new file mode 100644
index 0000000..74962a9
--- /dev/null
+++ b/examples/csharp/helloworld-from-cli/GreeterClient/project.json
@@ -0,0 +1,26 @@
+{
+  "title": "GreeterClient",
+  "version": "1.0.0-*",
+  "buildOptions": {
+    "debugType": "portable",
+    "emitEntryPoint": "true"
+  },
+  "dependencies": {
+    "Google.Protobuf": "3.0.0",
+    "Grpc": "1.0.0",
+    "Greeter": {
+      "target": "project"
+    }
+  },
+  "frameworks": {
+    "net45": {
+      "frameworkAssemblies": {
+        "System.Runtime": "",
+        "System.IO": ""
+      },
+      "dependencies": {
+	"Microsoft.NETCore.Platforms": "1.0.1" 
+      }
+    }
+  }
+}
diff --git a/examples/csharp/helloworld-from-cli/GreeterServer/Program.cs b/examples/csharp/helloworld-from-cli/GreeterServer/Program.cs
new file mode 100644
index 0000000..fdab379
--- /dev/null
+++ b/examples/csharp/helloworld-from-cli/GreeterServer/Program.cs
@@ -0,0 +1,66 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+using System;
+using System.Threading.Tasks;
+using Grpc.Core;
+using Helloworld;
+
+namespace GreeterServer
+{
+    class GreeterImpl : Greeter.GreeterBase
+    {
+        // Server side handler of the SayHello RPC
+        public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
+        {
+            return Task.FromResult(new HelloReply { Message = "Hello " + request.Name });
+        }
+    }
+
+    class Program
+    {
+        const int Port = 50051;
+
+        public static void Main(string[] args)
+        {
+            Server server = new Server
+            {
+                Services = { Greeter.BindService(new GreeterImpl()) },
+                Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) }
+            };
+            server.Start();
+
+            Console.WriteLine("Greeter server listening on port " + Port);
+            Console.WriteLine("Press any key to stop the server...");
+            Console.ReadKey();
+
+            server.ShutdownAsync().Wait();
+        }
+    }
+}
diff --git a/examples/csharp/helloworld-from-cli/GreeterServer/project.json b/examples/csharp/helloworld-from-cli/GreeterServer/project.json
new file mode 100644
index 0000000..33af254
--- /dev/null
+++ b/examples/csharp/helloworld-from-cli/GreeterServer/project.json
@@ -0,0 +1,26 @@
+{
+  "title": "GreeterServer",
+  "version": "1.0.0-*",
+  "buildOptions": {
+    "debugType": "portable",
+    "emitEntryPoint": "true"
+  },
+  "dependencies": {
+    "Google.Protobuf": "3.0.0",
+    "Grpc": "1.0.0",
+    "Greeter": {
+      "target": "project"
+    }
+  },
+  "frameworks": {
+    "net45": {
+      "frameworkAssemblies": {
+        "System.Runtime": "",
+        "System.IO": ""
+      },
+      "dependencies": {
+	"Microsoft.NETCore.Platforms": "1.0.1" 
+      }
+    }
+  }
+}
diff --git a/examples/csharp/helloworld-from-cli/README.md b/examples/csharp/helloworld-from-cli/README.md
new file mode 100644
index 0000000..4db0776
--- /dev/null
+++ b/examples/csharp/helloworld-from-cli/README.md
@@ -0,0 +1,59 @@
+gRPC in 3 minutes (C#)
+========================
+
+BACKGROUND
+-------------
+This is a different version of the helloworld example, using the dotnet sdk
+tools to build and run.
+
+For this sample, we've already generated the server and client stubs from [helloworld.proto][].
+
+Example projects in this directory depend on the [Grpc](https://www.nuget.org/packages/Grpc/)
+and [Google.Protobuf](https://www.nuget.org/packages/Google.Protobuf/) NuGet packages
+which have been already added to the project for you.
+
+The examples in this directory target .NET 4.5 framework, as .NET Core support is
+currently experimental.
+
+PREREQUISITES
+-------------
+
+- The DotNetCore SDK cli.
+
+- The .NET 4.5 framework.
+
+Both are available to download at https://www.microsoft.com/net/download
+
+BUILD
+-------
+
+From the `examples/csharp/helloworld-from-cli` directory:
+
+- `dotnet restore`
+
+- `dotnet build **/project.json` (this will automatically download NuGet dependencies)
+
+Try it!
+-------
+
+- Run the server
+
+  ```
+  > cd GreeterServer
+  > dotnet run
+  ```
+
+- Run the client
+
+  ```
+  > cd GreeterClient
+  > dotnet run
+  ```
+
+Tutorial
+--------
+
+You can find a more detailed tutorial about Grpc in [gRPC Basics: C#][]
+
+[helloworld.proto]:../../protos/helloworld.proto
+[gRPC Basics: C#]:http://www.grpc.io/docs/tutorials/basic/csharp.html
diff --git a/examples/csharp/helloworld/.nuget/packages.config b/examples/csharp/helloworld/.nuget/packages.config
deleted file mode 100644
index aa06080..0000000
--- a/examples/csharp/helloworld/.nuget/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="Grpc.Tools" version="0.15.0" />
-</packages>
\ No newline at end of file
diff --git a/examples/csharp/helloworld/Greeter.sln b/examples/csharp/helloworld/Greeter.sln
index 9430e94..49e364d 100644
--- a/examples/csharp/helloworld/Greeter.sln
+++ b/examples/csharp/helloworld/Greeter.sln
@@ -9,11 +9,6 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GreeterClient", "GreeterClient\GreeterClient.csproj", "{ACCF4597-3748-4117-8633-1CB767F8CCC3}"
 EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{FF1EBE95-F20D-4C27-8A61-D0125F3C8152}"
-	ProjectSection(SolutionItems) = preProject
-		.nuget\packages.config = .nuget\packages.config
-	EndProjectSection
-EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
diff --git a/examples/csharp/helloworld/Greeter/Greeter.csproj b/examples/csharp/helloworld/Greeter/Greeter.csproj
index 20b85db..54a41d1 100644
--- a/examples/csharp/helloworld/Greeter/Greeter.csproj
+++ b/examples/csharp/helloworld/Greeter/Greeter.csproj
@@ -33,16 +33,16 @@
   <ItemGroup>
     <Reference Include="Google.Protobuf, Version=3.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Protobuf.3.0.0-beta3\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
     </Reference>
     <Reference Include="Grpc.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Grpc.Core.0.15.0\lib\net45\Grpc.Core.dll</HintPath>
+      <HintPath>..\packages\Grpc.Core.1.0.0\lib\net45\Grpc.Core.dll</HintPath>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Interactive.Async, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Ix-Async.1.2.5\lib\net45\System.Interactive.Async.dll</HintPath>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
     </Reference>
   </ItemGroup>
   <ItemGroup>
@@ -61,11 +61,11 @@
     <None Include="packages.config" />
   </ItemGroup>
   <ItemGroup />
-  <Import Project="..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets')" />
+  <Import Project="..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets')" />
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
       <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
     </PropertyGroup>
-    <Error Condition="!Exists('..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets'))" />
+    <Error Condition="!Exists('..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets'))" />
   </Target>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/examples/csharp/helloworld/Greeter/Properties/AssemblyInfo.cs b/examples/csharp/helloworld/Greeter/Properties/AssemblyInfo.cs
index b9d0dc2..d6f554b 100644
--- a/examples/csharp/helloworld/Greeter/Properties/AssemblyInfo.cs
+++ b/examples/csharp/helloworld/Greeter/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/examples/csharp/helloworld/Greeter/packages.config b/examples/csharp/helloworld/Greeter/packages.config
index ff9d6bb..46f7dda 100644
--- a/examples/csharp/helloworld/Greeter/packages.config
+++ b/examples/csharp/helloworld/Greeter/packages.config
@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Google.Protobuf" version="3.0.0-beta3" targetFramework="net45" />
-  <package id="Grpc" version="0.15.0" targetFramework="net45" />
-  <package id="Grpc.Core" version="0.15.0" targetFramework="net45" />
-  <package id="Ix-Async" version="1.2.5" targetFramework="net45" />
-</packages>
\ No newline at end of file
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
+  <package id="Grpc" version="1.0.0" targetFramework="net45" />
+  <package id="Grpc.Core" version="1.0.0" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
+  <package id="Grpc.Tools" version="1.0.0" targetFramework="net45" />
+</packages>
diff --git a/examples/csharp/helloworld/GreeterClient/GreeterClient.csproj b/examples/csharp/helloworld/GreeterClient/GreeterClient.csproj
index 2b38ce2..43d85e1 100644
--- a/examples/csharp/helloworld/GreeterClient/GreeterClient.csproj
+++ b/examples/csharp/helloworld/GreeterClient/GreeterClient.csproj
@@ -33,16 +33,16 @@
   <ItemGroup>
     <Reference Include="Google.Protobuf, Version=3.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Protobuf.3.0.0-beta3\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
     </Reference>
     <Reference Include="Grpc.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Grpc.Core.0.15.0\lib\net45\Grpc.Core.dll</HintPath>
+      <HintPath>..\packages\Grpc.Core.1.0.0\lib\net45\Grpc.Core.dll</HintPath>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Interactive.Async, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Ix-Async.1.2.5\lib\net45\System.Interactive.Async.dll</HintPath>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
     </Reference>
   </ItemGroup>
   <ItemGroup>
@@ -59,11 +59,11 @@
   <ItemGroup>
     <None Include="packages.config" />
   </ItemGroup>
-  <Import Project="..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets')" />
+  <Import Project="..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets')" />
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
       <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
     </PropertyGroup>
-    <Error Condition="!Exists('..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets'))" />
+    <Error Condition="!Exists('..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets'))" />
   </Target>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/examples/csharp/helloworld/GreeterClient/Properties/AssemblyInfo.cs b/examples/csharp/helloworld/GreeterClient/Properties/AssemblyInfo.cs
index 1422e95..7e35696 100644
--- a/examples/csharp/helloworld/GreeterClient/Properties/AssemblyInfo.cs
+++ b/examples/csharp/helloworld/GreeterClient/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/examples/csharp/helloworld/GreeterClient/packages.config b/examples/csharp/helloworld/GreeterClient/packages.config
index ff9d6bb..9f55097 100644
--- a/examples/csharp/helloworld/GreeterClient/packages.config
+++ b/examples/csharp/helloworld/GreeterClient/packages.config
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Google.Protobuf" version="3.0.0-beta3" targetFramework="net45" />
-  <package id="Grpc" version="0.15.0" targetFramework="net45" />
-  <package id="Grpc.Core" version="0.15.0" targetFramework="net45" />
-  <package id="Ix-Async" version="1.2.5" targetFramework="net45" />
-</packages>
\ No newline at end of file
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
+  <package id="Grpc" version="1.0.0" targetFramework="net45" />
+  <package id="Grpc.Core" version="1.0.0" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
+</packages>
diff --git a/examples/csharp/helloworld/GreeterServer/GreeterServer.csproj b/examples/csharp/helloworld/GreeterServer/GreeterServer.csproj
index 43c6336..5a3457f 100644
--- a/examples/csharp/helloworld/GreeterServer/GreeterServer.csproj
+++ b/examples/csharp/helloworld/GreeterServer/GreeterServer.csproj
@@ -33,16 +33,16 @@
   <ItemGroup>
     <Reference Include="Google.Protobuf, Version=3.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Protobuf.3.0.0-beta3\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
     </Reference>
     <Reference Include="Grpc.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Grpc.Core.0.15.0\lib\net45\Grpc.Core.dll</HintPath>
+      <HintPath>..\packages\Grpc.Core.1.0.0\lib\net45\Grpc.Core.dll</HintPath>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Interactive.Async, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Ix-Async.1.2.5\lib\net45\System.Interactive.Async.dll</HintPath>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
     </Reference>
   </ItemGroup>
   <ItemGroup>
@@ -59,11 +59,11 @@
   <ItemGroup>
     <None Include="packages.config" />
   </ItemGroup>
-  <Import Project="..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets')" />
+  <Import Project="..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets')" />
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
       <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
     </PropertyGroup>
-    <Error Condition="!Exists('..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets'))" />
+    <Error Condition="!Exists('..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets'))" />
   </Target>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/examples/csharp/helloworld/GreeterServer/Properties/AssemblyInfo.cs b/examples/csharp/helloworld/GreeterServer/Properties/AssemblyInfo.cs
index f8a8d52..b4444ed 100644
--- a/examples/csharp/helloworld/GreeterServer/Properties/AssemblyInfo.cs
+++ b/examples/csharp/helloworld/GreeterServer/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/examples/csharp/helloworld/GreeterServer/packages.config b/examples/csharp/helloworld/GreeterServer/packages.config
index ff9d6bb..9f55097 100644
--- a/examples/csharp/helloworld/GreeterServer/packages.config
+++ b/examples/csharp/helloworld/GreeterServer/packages.config
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Google.Protobuf" version="3.0.0-beta3" targetFramework="net45" />
-  <package id="Grpc" version="0.15.0" targetFramework="net45" />
-  <package id="Grpc.Core" version="0.15.0" targetFramework="net45" />
-  <package id="Ix-Async" version="1.2.5" targetFramework="net45" />
-</packages>
\ No newline at end of file
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
+  <package id="Grpc" version="1.0.0" targetFramework="net45" />
+  <package id="Grpc.Core" version="1.0.0" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
+</packages>
diff --git a/examples/csharp/helloworld/README.md b/examples/csharp/helloworld/README.md
index d13c9ac..71840ad 100644
--- a/examples/csharp/helloworld/README.md
+++ b/examples/csharp/helloworld/README.md
@@ -13,7 +13,7 @@
 -------------
 
 - Windows: .NET Framework 4.5+, Visual Studio 2013 or 2015
-- Linux: Mono 4+, MonoDevelop 5.9+ (with NuGet add-in installed)
+- Linux: Mono 4+, MonoDevelop 5.9+
 - Mac OS X: Xamarin Studio 5.9+
 
 BUILD
@@ -21,7 +21,20 @@
 
 - Open solution `Greeter.sln` with Visual Studio, Monodevelop (on Linux) or Xamarin Studio (on Mac OS X)
 
-- Build the solution (this will automatically download NuGet dependencies)
+# Using Visual Studio
+
+* Build the solution (this will automatically download NuGet dependencies)
+
+# Using Monodevelop or Xamarin Studio
+
+The nuget add-in available for Xamarin Studio and Monodevelop IDEs is too old to 
+download all of the nuget dependencies of gRPC. One alternative to is to use the dotnet command line tools instead (see [helloworld-from-cli]).
+
+Using these IDEs, a workaround is as follows:
+* Obtain a nuget executable for your platform and update it with
+ `nuget update -self`. 
+* Navigate to this directory and run `nuget restore`.
+* Now that packages have been restored into their proper package folder, build the solution from your IDE.
 
 Try it!
 -------
@@ -49,5 +62,6 @@
 
 You can find a more detailed tutorial in [gRPC Basics: C#][]
 
+[helloworld-from-cli]:../helloworld-from-cli/README.md
 [helloworld.proto]:../../protos/helloworld.proto
 [gRPC Basics: C#]:http://www.grpc.io/docs/tutorials/basic/csharp.html
diff --git a/examples/csharp/helloworld/generate_protos.bat b/examples/csharp/helloworld/generate_protos.bat
index a952bb4..28b7395 100644
--- a/examples/csharp/helloworld/generate_protos.bat
+++ b/examples/csharp/helloworld/generate_protos.bat
@@ -34,8 +34,8 @@
 @rem enter this directory
 cd /d %~dp0
 
-set TOOLS_PATH=packages\Grpc.Tools.0.15.0\tools\windows_x86
+set TOOLS_PATH=packages\Grpc.Tools.1.0.0\tools\windows_x86
 
 %TOOLS_PATH%\protoc.exe -I../../protos --csharp_out Greeter  ../../protos/helloworld.proto --grpc_out Greeter --plugin=protoc-gen-grpc=%TOOLS_PATH%\grpc_csharp_plugin.exe
 
-endlocal
\ No newline at end of file
+endlocal
diff --git a/examples/csharp/route_guide/.nuget/packages.config b/examples/csharp/route_guide/.nuget/packages.config
deleted file mode 100644
index aa06080..0000000
--- a/examples/csharp/route_guide/.nuget/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="Grpc.Tools" version="0.15.0" />
-</packages>
\ No newline at end of file
diff --git a/examples/csharp/route_guide/RouteGuide.sln b/examples/csharp/route_guide/RouteGuide.sln
index 0b79fdc..00065b0 100644
--- a/examples/csharp/route_guide/RouteGuide.sln
+++ b/examples/csharp/route_guide/RouteGuide.sln
@@ -9,11 +9,6 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RouteGuideServer", "RouteGuideServer\RouteGuideServer.csproj", "{4B7C7794-BE24-4477-ACE7-18259EB73D27}"
 EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{2F6B184B-A576-4F21-AF2E-27E73D1FC96E}"
-	ProjectSection(SolutionItems) = preProject
-		.nuget\packages.config = .nuget\packages.config
-	EndProjectSection
-EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
diff --git a/examples/csharp/route_guide/RouteGuide/Properties/AssemblyInfo.cs b/examples/csharp/route_guide/RouteGuide/Properties/AssemblyInfo.cs
index 9c3441e..63a8854 100644
--- a/examples/csharp/route_guide/RouteGuide/Properties/AssemblyInfo.cs
+++ b/examples/csharp/route_guide/RouteGuide/Properties/AssemblyInfo.cs
@@ -1,4 +1,37 @@
-using System.Reflection;
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
diff --git a/examples/csharp/route_guide/RouteGuide/RouteGuide.csproj b/examples/csharp/route_guide/RouteGuide/RouteGuide.csproj
index 601d16b..02bca2f 100644
--- a/examples/csharp/route_guide/RouteGuide/RouteGuide.csproj
+++ b/examples/csharp/route_guide/RouteGuide/RouteGuide.csproj
@@ -33,11 +33,11 @@
   <ItemGroup>
     <Reference Include="Google.Protobuf, Version=3.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Protobuf.3.0.0-beta3\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
     </Reference>
     <Reference Include="Grpc.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Grpc.Core.0.15.0\lib\net45\Grpc.Core.dll</HintPath>
+      <HintPath>..\packages\Grpc.Core.1.0.0\lib\net45\Grpc.Core.dll</HintPath>
     </Reference>
     <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
@@ -45,15 +45,15 @@
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
-    <Reference Include="System.Interactive.Async, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Ix-Async.1.2.5\lib\net45\System.Interactive.Async.dll</HintPath>
-    </Reference>
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Data" />
     <Reference Include="System.Xml" />
+    <Reference Include="System.Interactive.Async, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Properties\AssemblyInfo.cs" />
@@ -74,12 +74,12 @@
     </None>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
-  <Import Project="..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets')" />
+  <Import Project="..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets')" />
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
       <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
     </PropertyGroup>
-    <Error Condition="!Exists('..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets'))" />
+    <Error Condition="!Exists('..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets'))" />
   </Target>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
@@ -88,4 +88,4 @@
   <Target Name="AfterBuild">
   </Target>
   -->
-</Project>
\ No newline at end of file
+</Project>
diff --git a/examples/csharp/route_guide/RouteGuide/packages.config b/examples/csharp/route_guide/RouteGuide/packages.config
index b962a72..4e8d3f2 100644
--- a/examples/csharp/route_guide/RouteGuide/packages.config
+++ b/examples/csharp/route_guide/RouteGuide/packages.config
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Google.Protobuf" version="3.0.0-beta3" targetFramework="net45" />
-  <package id="Grpc" version="0.15.0" targetFramework="net45" />
-  <package id="Grpc.Core" version="0.15.0" targetFramework="net45" />
-  <package id="Ix-Async" version="1.2.5" targetFramework="net45" />
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
+  <package id="Grpc" version="1.0.0" targetFramework="net45" />
+  <package id="Grpc.Core" version="1.0.0" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
   <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
-</packages>
\ No newline at end of file
+</packages>
diff --git a/examples/csharp/route_guide/RouteGuideClient/Properties/AssemblyInfo.cs b/examples/csharp/route_guide/RouteGuideClient/Properties/AssemblyInfo.cs
index a17e164..ba72fd2 100644
--- a/examples/csharp/route_guide/RouteGuideClient/Properties/AssemblyInfo.cs
+++ b/examples/csharp/route_guide/RouteGuideClient/Properties/AssemblyInfo.cs
@@ -1,4 +1,37 @@
-using System.Reflection;
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
diff --git a/examples/csharp/route_guide/RouteGuideClient/RouteGuideClient.csproj b/examples/csharp/route_guide/RouteGuideClient/RouteGuideClient.csproj
index 6f5c0a5..4792c52 100644
--- a/examples/csharp/route_guide/RouteGuideClient/RouteGuideClient.csproj
+++ b/examples/csharp/route_guide/RouteGuideClient/RouteGuideClient.csproj
@@ -35,11 +35,11 @@
   <ItemGroup>
     <Reference Include="Google.Protobuf, Version=3.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Protobuf.3.0.0-beta3\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
     </Reference>
     <Reference Include="Grpc.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Grpc.Core.0.15.0\lib\net45\Grpc.Core.dll</HintPath>
+      <HintPath>..\packages\Grpc.Core.1.0.0\lib\net45\Grpc.Core.dll</HintPath>
     </Reference>
     <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
@@ -49,7 +49,7 @@
     <Reference Include="System.Core" />
     <Reference Include="System.Interactive.Async, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Ix-Async.1.2.5\lib\net45\System.Interactive.Async.dll</HintPath>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
     </Reference>
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
@@ -71,12 +71,12 @@
     </ProjectReference>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
-  <Import Project="..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets')" />
+  <Import Project="..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets')" />
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
       <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
     </PropertyGroup>
-    <Error Condition="!Exists('..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets'))" />
+    <Error Condition="!Exists('..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets'))" />
   </Target>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
@@ -85,4 +85,4 @@
   <Target Name="AfterBuild">
   </Target>
   -->
-</Project>
\ No newline at end of file
+</Project>
diff --git a/examples/csharp/route_guide/RouteGuideClient/packages.config b/examples/csharp/route_guide/RouteGuideClient/packages.config
index b962a72..4e8d3f2 100644
--- a/examples/csharp/route_guide/RouteGuideClient/packages.config
+++ b/examples/csharp/route_guide/RouteGuideClient/packages.config
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Google.Protobuf" version="3.0.0-beta3" targetFramework="net45" />
-  <package id="Grpc" version="0.15.0" targetFramework="net45" />
-  <package id="Grpc.Core" version="0.15.0" targetFramework="net45" />
-  <package id="Ix-Async" version="1.2.5" targetFramework="net45" />
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
+  <package id="Grpc" version="1.0.0" targetFramework="net45" />
+  <package id="Grpc.Core" version="1.0.0" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
   <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
-</packages>
\ No newline at end of file
+</packages>
diff --git a/examples/csharp/route_guide/RouteGuideServer/Properties/AssemblyInfo.cs b/examples/csharp/route_guide/RouteGuideServer/Properties/AssemblyInfo.cs
index a161b1d..0d78150 100644
--- a/examples/csharp/route_guide/RouteGuideServer/Properties/AssemblyInfo.cs
+++ b/examples/csharp/route_guide/RouteGuideServer/Properties/AssemblyInfo.cs
@@ -1,4 +1,37 @@
-using System.Reflection;
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
diff --git a/examples/csharp/route_guide/RouteGuideServer/RouteGuideServer.csproj b/examples/csharp/route_guide/RouteGuideServer/RouteGuideServer.csproj
index 5bf46b0..b35e576 100644
--- a/examples/csharp/route_guide/RouteGuideServer/RouteGuideServer.csproj
+++ b/examples/csharp/route_guide/RouteGuideServer/RouteGuideServer.csproj
@@ -35,11 +35,11 @@
   <ItemGroup>
     <Reference Include="Google.Protobuf, Version=3.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Protobuf.3.0.0-beta3\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
     </Reference>
     <Reference Include="Grpc.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Grpc.Core.0.15.0\lib\net45\Grpc.Core.dll</HintPath>
+      <HintPath>..\packages\Grpc.Core.1.0.0\lib\net45\Grpc.Core.dll</HintPath>
     </Reference>
     <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
@@ -47,15 +47,15 @@
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
-    <Reference Include="System.Interactive.Async, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Ix-Async.1.2.5\lib\net45\System.Interactive.Async.dll</HintPath>
-    </Reference>
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Data" />
     <Reference Include="System.Xml" />
+    <Reference Include="System.Interactive.Async, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Program.cs" />
@@ -72,12 +72,12 @@
     </ProjectReference>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
-  <Import Project="..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets')" />
+  <Import Project="..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets')" />
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
       <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
     </PropertyGroup>
-    <Error Condition="!Exists('..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.0.15.0\build\net45\Grpc.Core.targets'))" />
+    <Error Condition="!Exists('..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.1.0.0\build\net45\Grpc.Core.targets'))" />
   </Target>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
@@ -86,4 +86,4 @@
   <Target Name="AfterBuild">
   </Target>
   -->
-</Project>
\ No newline at end of file
+</Project>
diff --git a/examples/csharp/route_guide/RouteGuideServer/packages.config b/examples/csharp/route_guide/RouteGuideServer/packages.config
index b962a72..a742eb2 100644
--- a/examples/csharp/route_guide/RouteGuideServer/packages.config
+++ b/examples/csharp/route_guide/RouteGuideServer/packages.config
@@ -1,8 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Google.Protobuf" version="3.0.0-beta3" targetFramework="net45" />
-  <package id="Grpc" version="0.15.0" targetFramework="net45" />
-  <package id="Grpc.Core" version="0.15.0" targetFramework="net45" />
-  <package id="Ix-Async" version="1.2.5" targetFramework="net45" />
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
+  <package id="Grpc" version="1.0.0" targetFramework="net45" />
+  <package id="Grpc.Core" version="1.0.0" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
   <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
-</packages>
\ No newline at end of file
+  <package id="Grpc.Tools" version="1.0.0" targetFramework="net45" />
+</packages>
diff --git a/examples/csharp/route_guide/generate_protos.bat b/examples/csharp/route_guide/generate_protos.bat
index dbdbd1f..69f5e0f 100644
--- a/examples/csharp/route_guide/generate_protos.bat
+++ b/examples/csharp/route_guide/generate_protos.bat
@@ -34,8 +34,8 @@
 @rem enter this directory
 cd /d %~dp0
 
-set TOOLS_PATH=packages\Grpc.Tools.0.15.0\tools\windows_x86
+set TOOLS_PATH=packages\Grpc.Tools.1.0.0\tools\windows_x86
 
 %TOOLS_PATH%\protoc.exe -I../../protos --csharp_out RouteGuide  ../../protos/route_guide.proto --grpc_out RouteGuide --plugin=protoc-gen-grpc=%TOOLS_PATH%\grpc_csharp_plugin.exe
 
-endlocal
\ No newline at end of file
+endlocal
diff --git a/examples/node/README.md b/examples/node/README.md
index b92ca38..f29236c 100644
--- a/examples/node/README.md
+++ b/examples/node/README.md
@@ -39,7 +39,7 @@
    $ # from this directory
    $ node ./dynamic_codegen/greeter_client.js
    $ # OR
-   $ node ./dynamic_codegen/greeter_client.js
+   $ node ./static_codegen/greeter_client.js
    ```
 
 TUTORIAL
diff --git a/examples/node/package.json b/examples/node/package.json
index 2cae031..6317838 100644
--- a/examples/node/package.json
+++ b/examples/node/package.json
@@ -3,8 +3,8 @@
   "version": "0.1.0",
   "dependencies": {
     "async": "^1.5.2",
-    "google-protobuf": "^3.0.0-alpha.5",
-    "grpc": "^0.14.0",
+    "google-protobuf": "^3.0.0",
+    "grpc": "^1.0.0",
     "lodash": "^4.6.1",
     "minimist": "^1.2.0"
   }
diff --git a/examples/node/static_codegen/greeter_client.js b/examples/node/static_codegen/greeter_client.js
index da80cf3..9b93e00 100644
--- a/examples/node/static_codegen/greeter_client.js
+++ b/examples/node/static_codegen/greeter_client.js
@@ -39,13 +39,13 @@
 function main() {
   var client = new services.GreeterClient('localhost:50051',
                                           grpc.credentials.createInsecure());
+  var request = new messages.HelloRequest();
   var user;
   if (process.argv.length >= 3) {
     user = process.argv[2];
   } else {
     user = 'world';
   }
-  var request = new messages.HelloRequest();
   request.setName(user);
   client.sayHello(request, function(err, response) {
     console.log('Greeting:', response.getMessage());
diff --git a/examples/objective-c/auth_sample/AuthTestService.podspec b/examples/objective-c/auth_sample/AuthTestService.podspec
index d709c7e..59fbb5f 100644
--- a/examples/objective-c/auth_sample/AuthTestService.podspec
+++ b/examples/objective-c/auth_sample/AuthTestService.podspec
@@ -14,7 +14,7 @@
   src = "../../protos"
 
   # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
-  s.dependency "!ProtoCompiler-gRPCPlugin", "~> 1.0.0-pre1"
+  s.dependency "!ProtoCompiler-gRPCPlugin", "~> 1.0"
 
   # Pods directory corresponding to this app's Podfile, relative to the location of this podspec.
   pods_root = 'Pods'
@@ -45,10 +45,6 @@
     ms.requires_arc = false
     # The generated files depend on the protobuf runtime.
     ms.dependency "Protobuf"
-    # This is needed by all pods that depend on Protobuf:
-    ms.pod_target_xcconfig = {
-      'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
-    }
   end
 
   # Files generated by the gRPC plugin
@@ -60,4 +56,11 @@
     ss.dependency "gRPC-ProtoRPC"
     ss.dependency "#{s.name}/Messages"
   end
+
+  s.pod_target_xcconfig = {
+    # This is needed by all pods that depend on Protobuf:
+    'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
+    # This is needed by all pods that depend on gRPC-RxLibrary:
+    'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
+  }
 end
diff --git a/examples/objective-c/helloworld/HelloWorld.podspec b/examples/objective-c/helloworld/HelloWorld.podspec
index 48364fc..96a8e2e 100644
--- a/examples/objective-c/helloworld/HelloWorld.podspec
+++ b/examples/objective-c/helloworld/HelloWorld.podspec
@@ -14,7 +14,7 @@
   src = "../../protos"
 
   # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
-  s.dependency "!ProtoCompiler-gRPCPlugin", "~> 1.0.0-pre1"
+  s.dependency "!ProtoCompiler-gRPCPlugin", "~> 1.0"
 
   # Pods directory corresponding to this app's Podfile, relative to the location of this podspec.
   pods_root = 'Pods'
@@ -45,10 +45,6 @@
     ms.requires_arc = false
     # The generated files depend on the protobuf runtime.
     ms.dependency "Protobuf"
-    # This is needed by all pods that depend on Protobuf:
-    ms.pod_target_xcconfig = {
-      'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
-    }
   end
 
   # Files generated by the gRPC plugin
@@ -60,4 +56,11 @@
     ss.dependency "gRPC-ProtoRPC"
     ss.dependency "#{s.name}/Messages"
   end
+
+  s.pod_target_xcconfig = {
+    # This is needed by all pods that depend on Protobuf:
+    'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
+    # This is needed by all pods that depend on gRPC-RxLibrary:
+    'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
+  }
 end
diff --git a/examples/objective-c/route_guide/RouteGuide.podspec b/examples/objective-c/route_guide/RouteGuide.podspec
index 04bc10b..768b5bf 100644
--- a/examples/objective-c/route_guide/RouteGuide.podspec
+++ b/examples/objective-c/route_guide/RouteGuide.podspec
@@ -14,7 +14,7 @@
   src = "../../protos"
 
   # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
-  s.dependency "!ProtoCompiler-gRPCPlugin", "~> 1.0.0-pre1"
+  s.dependency "!ProtoCompiler-gRPCPlugin", "~> 1.0"
 
   # Pods directory corresponding to this app's Podfile, relative to the location of this podspec.
   pods_root = 'Pods'
@@ -45,10 +45,6 @@
     ms.requires_arc = false
     # The generated files depend on the protobuf runtime.
     ms.dependency "Protobuf"
-    # This is needed by all pods that depend on Protobuf:
-    ms.pod_target_xcconfig = {
-      'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
-    }
   end
 
   # Files generated by the gRPC plugin
@@ -60,4 +56,11 @@
     ss.dependency "gRPC-ProtoRPC"
     ss.dependency "#{s.name}/Messages"
   end
+
+  s.pod_target_xcconfig = {
+    # This is needed by all pods that depend on Protobuf:
+    'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
+    # This is needed by all pods that depend on gRPC-RxLibrary:
+    'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
+  }
 end
diff --git a/examples/php/composer.json b/examples/php/composer.json
index d40b5db..e6409f8 100644
--- a/examples/php/composer.json
+++ b/examples/php/composer.json
@@ -1,8 +1,7 @@
 {
   "name": "grpc/grpc-demo",
   "description": "gRPC example for PHP",
-  "minimum-stability": "dev",
   "require": {
-    "grpc/grpc": "v0.15.2"
+    "grpc/grpc": "v1.0.0"
   }
 }
diff --git a/examples/php/route_guide/README.md b/examples/php/route_guide/README.md
index 4e74a79..26f1704 100644
--- a/examples/php/route_guide/README.md
+++ b/examples/php/route_guide/README.md
@@ -1,6 +1,6 @@
 #gRPC Basics: PHP sample code
 
 The files in this folder are the samples used in [gRPC Basics: PHP][],
-a detailed tutorial for using gRPC in Ruby.
+a detailed tutorial for using gRPC in PHP.
 
 [gRPC Basics: PHP]:http://www.grpc.io/docs/tutorials/basic/php.html
diff --git a/examples/php/route_guide/run_route_guide_client.sh b/examples/php/route_guide/run_route_guide_client.sh
index 90ecc62..10e6f00 100755
--- a/examples/php/route_guide/run_route_guide_client.sh
+++ b/examples/php/route_guide/run_route_guide_client.sh
@@ -30,5 +30,6 @@
 
 set -e
 cd $(dirname $0)
-php $extension_dir -d extension=grpc.so -d max_execution_time=300 \
-  route_guide_client.php ../../node/route_guide/route_guide_db.json
+php -d extension=grpc.so -d max_execution_time=300 \
+  route_guide_client.php \
+  ../../node/static_codegen/route_guide/route_guide_db.json
diff --git a/examples/php/run_greeter_client.sh b/examples/php/run_greeter_client.sh
index 1a6e9ee..4d0be4f2 100755
--- a/examples/php/run_greeter_client.sh
+++ b/examples/php/run_greeter_client.sh
@@ -30,5 +30,5 @@
 
 set -e
 cd $(dirname $0)
-php $extension_dir -d extension=grpc.so -d max_execution_time=300 \
+php -d extension=grpc.so -d max_execution_time=300 \
   greeter_client.php $1
diff --git a/examples/python/README.md b/examples/python/README.md
index 9992baa..d801d0d 100644
--- a/examples/python/README.md
+++ b/examples/python/README.md
@@ -1,56 +1 @@
-gRPC in 3 minutes (Python)
-========================
-
-Background
--------------
-For this sample, we've already generated the server and client stubs from
-[helloworld.proto][] and we'll be using a specific reference platform.
-
-
-Install gRPC:
-```sh
-  $ pip install grpcio
-```
-Or, to install it system wide:
-```sh
-  $ sudo pip install grpcio
-```
-
-If you're on Windows, make sure you installed the `pip.exe` component when you
-installed Python. Invoke as above but with `pip.exe` instead of `pip` (you may
-also need to invoke from a `cmd.exe` ran as administrator):
-```sh
-  $ pip.exe install grpcio
-```
-
-Download the example
-```sh
-  $ # Clone the repository to get the example code:
-  $ git clone https://github.com/grpc/grpc
-  $ # Navigate to the "hello, world" Python example:
-  $ cd grpc/examples/python/helloworld
-  ```
-
-Try it!
--------
-
-- Run the server
-
-  ```sh
-  $ python2.7 greeter_server.py &
-  ```
-
-- Run the client
-
-  ```sh
-  $ python2.7 greeter_client.py
-  ```
-
-Tutorial
---------
-
-You can find a more detailed tutorial in [gRPC Basics: Python][]
-
-[helloworld.proto]:../protos/helloworld.proto
-[Install gRPC Python]:../../src/python#installation
-[gRPC Basics: Python]:http://www.grpc.io/docs/tutorials/basic/python.html
+[This code's documentation lives on the grpc.io site.](http://www.grpc.io/docs/quickstart/python.html)
diff --git a/examples/python/helloworld/README.md b/examples/python/helloworld/README.md
index e889863..d801d0d 100644
--- a/examples/python/helloworld/README.md
+++ b/examples/python/helloworld/README.md
@@ -1 +1 @@
-[This code's documentation lives on the grpc.io site.](http://www.grpc.io/docs)
+[This code's documentation lives on the grpc.io site.](http://www.grpc.io/docs/quickstart/python.html)
diff --git a/examples/python/multiplex/README.md b/examples/python/multiplex/README.md
index bad3a42..5931be3 100644
--- a/examples/python/multiplex/README.md
+++ b/examples/python/multiplex/README.md
@@ -1 +1,3 @@
 An example showing two stubs sharing a channel and two servicers sharing a server.
+
+More complete documentation lives at [grpc.io](http://www.grpc.io/docs/tutorials/basic/python.html).
diff --git a/examples/python/route_guide/route_guide_server.py b/examples/python/route_guide/route_guide_server.py
index 4e780a7..3ffe678 100644
--- a/examples/python/route_guide/route_guide_server.py
+++ b/examples/python/route_guide/route_guide_server.py
@@ -68,7 +68,7 @@
   R = 6371000; # metres
   return R * c;
 
-class RouteGuideServicer(route_guide_pb2.BetaRouteGuideServicer):
+class RouteGuideServicer(route_guide_pb2.RouteGuideServicer):
   """Provides methods that implement functionality of route guide server."""
 
   def __init__(self):
diff --git a/examples/ruby/greeter_client.rb b/examples/ruby/greeter_client.rb
index cb4aa19..1cdf79e 100755
--- a/examples/ruby/greeter_client.rb
+++ b/examples/ruby/greeter_client.rb
@@ -38,7 +38,7 @@
 $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
 
 require 'grpc'
-require 'helloworld_services'
+require 'helloworld_services_pb'
 
 def main
   stub = Helloworld::Greeter::Stub.new('localhost:50051', :this_channel_is_insecure)
diff --git a/examples/ruby/greeter_server.rb b/examples/ruby/greeter_server.rb
index 622513d..6d82043 100755
--- a/examples/ruby/greeter_server.rb
+++ b/examples/ruby/greeter_server.rb
@@ -38,7 +38,7 @@
 $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
 
 require 'grpc'
-require 'helloworld_services'
+require 'helloworld_services_pb'
 
 # GreeterServer is simple server that implements the Helloworld Greeter server.
 class GreeterServer < Helloworld::Greeter::Service
diff --git a/examples/ruby/grpc-demo.gemspec b/examples/ruby/grpc-demo.gemspec
index b1dfdae..e1b77a5 100644
--- a/examples/ruby/grpc-demo.gemspec
+++ b/examples/ruby/grpc-demo.gemspec
@@ -3,7 +3,7 @@
 
 Gem::Specification.new do |s|
   s.name          = 'grpc-demo'
-  s.version       = '0.11.0'
+  s.version       = '1.0.0'
   s.authors       = ['gRPC Authors']
   s.email         = 'temiola@google.com'
   s.homepage      = 'https://github.com/grpc/grpc'
@@ -17,7 +17,7 @@
   s.require_paths = ['lib']
   s.platform      = Gem::Platform::RUBY
 
-  s.add_dependency 'grpc', '~> 0.11'
+  s.add_dependency 'grpc', '~> 1.0.0'
 
   s.add_development_dependency 'bundler', '~> 1.7'
 end
diff --git a/examples/ruby/lib/helloworld.rb b/examples/ruby/lib/helloworld_pb.rb
similarity index 100%
rename from examples/ruby/lib/helloworld.rb
rename to examples/ruby/lib/helloworld_pb.rb
diff --git a/examples/ruby/lib/helloworld_services.rb b/examples/ruby/lib/helloworld_services.rb
deleted file mode 100644
index fbec667..0000000
--- a/examples/ruby/lib/helloworld_services.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-# Generated by the protocol buffer compiler.  DO NOT EDIT!
-# Source: helloworld.proto for package 'helloworld'
-# Original file comments:
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-
-require 'grpc'
-require 'helloworld'
-
-module Helloworld
-  module Greeter
-    # The greeting service definition.
-    class Service
-
-      include GRPC::GenericService
-
-      self.marshal_class_method = :encode
-      self.unmarshal_class_method = :decode
-      self.service_name = 'helloworld.Greeter'
-
-      # Sends a greeting
-      rpc :SayHello, HelloRequest, HelloReply
-    end
-
-    Stub = Service.rpc_stub_class
-  end
-end
diff --git a/examples/ruby/lib/helloworld_services_pb.rb b/examples/ruby/lib/helloworld_services_pb.rb
new file mode 100644
index 0000000..4fee0aa
--- /dev/null
+++ b/examples/ruby/lib/helloworld_services_pb.rb
@@ -0,0 +1,54 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# Source: helloworld.proto for package 'helloworld'
+# Original file comments:
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+require 'grpc'
+require 'helloworld_pb'
+
+module Helloworld
+  module Greeter
+    # The greeting service definition.
+    class Service
+
+      include GRPC::GenericService
+
+      self.marshal_class_method = :encode
+      self.unmarshal_class_method = :decode
+      self.service_name = 'helloworld.Greeter'
+
+      # Sends a greeting
+      rpc :SayHello, HelloRequest, HelloReply
+    end
+
+    Stub = Service.rpc_stub_class
+  end
+end
diff --git a/examples/ruby/lib/route_guide.rb b/examples/ruby/lib/route_guide_pb.rb
similarity index 100%
rename from examples/ruby/lib/route_guide.rb
rename to examples/ruby/lib/route_guide_pb.rb
diff --git a/examples/ruby/lib/route_guide_services.rb b/examples/ruby/lib/route_guide_services.rb
deleted file mode 100644
index d8f123d..0000000
--- a/examples/ruby/lib/route_guide_services.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-# Generated by the protocol buffer compiler.  DO NOT EDIT!
-# Source: route_guide.proto for package 'routeguide'
-# Original file comments:
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-
-require 'grpc'
-require 'route_guide'
-
-module Routeguide
-  module RouteGuide
-    # Interface exported by the server.
-    class Service
-
-      include GRPC::GenericService
-
-      self.marshal_class_method = :encode
-      self.unmarshal_class_method = :decode
-      self.service_name = 'routeguide.RouteGuide'
-
-      # A simple RPC.
-      #
-      # Obtains the feature at a given position.
-      #
-      # A feature with an empty name is returned if there's no feature at the given
-      # position.
-      rpc :GetFeature, Point, Feature
-      # A server-to-client streaming RPC.
-      #
-      # Obtains the Features available within the given Rectangle.  Results are
-      # streamed rather than returned at once (e.g. in a response message with a
-      # repeated field), as the rectangle may cover a large area and contain a
-      # huge number of features.
-      rpc :ListFeatures, Rectangle, stream(Feature)
-      # A client-to-server streaming RPC.
-      #
-      # Accepts a stream of Points on a route being traversed, returning a
-      # RouteSummary when traversal is completed.
-      rpc :RecordRoute, stream(Point), RouteSummary
-      # A Bidirectional streaming RPC.
-      #
-      # Accepts a stream of RouteNotes sent while a route is being traversed,
-      # while receiving other RouteNotes (e.g. from other users).
-      rpc :RouteChat, stream(RouteNote), stream(RouteNote)
-    end
-
-    Stub = Service.rpc_stub_class
-  end
-end
diff --git a/examples/ruby/lib/route_guide_services_pb.rb b/examples/ruby/lib/route_guide_services_pb.rb
new file mode 100644
index 0000000..d43fcc6
--- /dev/null
+++ b/examples/ruby/lib/route_guide_services_pb.rb
@@ -0,0 +1,76 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# Source: route_guide.proto for package 'routeguide'
+# Original file comments:
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+require 'grpc'
+require 'route_guide_pb'
+
+module Routeguide
+  module RouteGuide
+    # Interface exported by the server.
+    class Service
+
+      include GRPC::GenericService
+
+      self.marshal_class_method = :encode
+      self.unmarshal_class_method = :decode
+      self.service_name = 'routeguide.RouteGuide'
+
+      # A simple RPC.
+      #
+      # Obtains the feature at a given position.
+      #
+      # A feature with an empty name is returned if there's no feature at the given
+      # position.
+      rpc :GetFeature, Point, Feature
+      # A server-to-client streaming RPC.
+      #
+      # Obtains the Features available within the given Rectangle.  Results are
+      # streamed rather than returned at once (e.g. in a response message with a
+      # repeated field), as the rectangle may cover a large area and contain a
+      # huge number of features.
+      rpc :ListFeatures, Rectangle, stream(Feature)
+      # A client-to-server streaming RPC.
+      #
+      # Accepts a stream of Points on a route being traversed, returning a
+      # RouteSummary when traversal is completed.
+      rpc :RecordRoute, stream(Point), RouteSummary
+      # A Bidirectional streaming RPC.
+      #
+      # Accepts a stream of RouteNotes sent while a route is being traversed,
+      # while receiving other RouteNotes (e.g. from other users).
+      rpc :RouteChat, stream(RouteNote), stream(RouteNote)
+    end
+
+    Stub = Service.rpc_stub_class
+  end
+end
diff --git a/examples/ruby/route_guide/route_guide_client.rb b/examples/ruby/route_guide/route_guide_client.rb
index e7f802c..01ea42c 100755
--- a/examples/ruby/route_guide/route_guide_client.rb
+++ b/examples/ruby/route_guide/route_guide_client.rb
@@ -39,7 +39,7 @@
 
 require 'grpc'
 require 'multi_json'
-require 'route_guide_services'
+require 'route_guide_services_pb'
 
 include Routeguide
 
@@ -140,10 +140,24 @@
 def run_route_chat(stub)
   p 'Route Chat'
   p '----------'
-  # TODO: decouple sending and receiving, i.e have the response enumerator run
-  # on its own thread.
-  resps = stub.route_chat(ROUTE_CHAT_NOTES)
-  resps.each { |r| p "received #{r.inspect}" }
+  sleeping_enumerator = SleepingEnumerator.new(ROUTE_CHAT_NOTES, 1)
+  stub.route_chat(sleeping_enumerator.each_item) { |r| p "received #{r.inspect}" }
+end
+
+# SleepingEnumerator yields through items, and sleeps between each one
+class SleepingEnumerator
+  def initialize(items, delay)
+    @items = items
+    @delay = delay
+  end
+  def each_item
+    return enum_for(:each_item) unless block_given?
+    @items.each do |item|
+      sleep @delay
+      p "next item to send is #{item.inspect}"
+      yield item
+    end
+  end
 end
 
 def main
diff --git a/examples/ruby/route_guide/route_guide_server.rb b/examples/ruby/route_guide/route_guide_server.rb
index bebe49b..41b9174 100755
--- a/examples/ruby/route_guide/route_guide_server.rb
+++ b/examples/ruby/route_guide/route_guide_server.rb
@@ -40,7 +40,7 @@
 
 require 'grpc'
 require 'multi_json'
-require 'route_guide_services'
+require 'route_guide_services_pb'
 
 include Routeguide
 COORD_FACTOR = 1e7
@@ -100,28 +100,6 @@
   end
 end
 
-# A EnumeratorQueue wraps a Queue to yield the items added to it.
-class EnumeratorQueue
-  extend Forwardable
-  def_delegators :@q, :push
-
-  def initialize(sentinel)
-    @q = Queue.new
-    @sentinel = sentinel
-    @received_notes = {}
-  end
-
-  def each_item
-    return enum_for(:each_item) unless block_given?
-    loop do
-      r = @q.pop
-      break if r.equal?(@sentinel)
-      fail r if r.is_a? Exception
-      yield r
-    end
-  end
-end
-
 # ServerImpl provides an implementation of the RouteGuide service.
 class ServerImpl < RouteGuide::Service
   # @param [Hash] feature_db {location => name}
@@ -166,28 +144,33 @@
   end
 
   def route_chat(notes)
-    q = EnumeratorQueue.new(self)
-    # run a separate thread that processes the incoming requests
-    t = Thread.new do
-      begin
-        notes.each do |n|
-          key = {
-            'latitude' => n.location.latitude,
-            'longitude' => n.location.longitude
-          }
-          earlier_msgs = @received_notes[key]
-          @received_notes[key] << n.message
-          # send back the earlier messages at this point
-          earlier_msgs.each do |r|
-            q.push(RouteNote.new(location: n.location, message: r))
-          end
+    RouteChatEnumerator.new(notes, @received_notes).each_item
+  end
+end
+
+class RouteChatEnumerator
+  def initialize(notes, received_notes)
+    @notes = notes
+    @received_notes = received_notes
+  end
+  def each_item
+    return enum_for(:each_item) unless block_given?
+    begin
+      @notes.each do |n|
+        key = {
+          'latitude' => n.location.latitude,
+          'longitude' => n.location.longitude
+        }
+        earlier_msgs = @received_notes[key]
+        @received_notes[key] << n.message
+        # send back the earlier messages at this point
+        earlier_msgs.each do |r|
+          yield RouteNote.new(location: n.location, message: r)
         end
-        q.push(self)  # signal completion
-      rescue StandardError => e
-        q.push(e)  # signal completion via an error
       end
+    rescue StandardError => e
+      fail e # signal completion via an error
     end
-    q.each_item
   end
 end
 
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index 10e9d00..6886bdf 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -35,7 +35,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-Core'
-  version = '1.0.0-pre1'
+  version = '1.0.0'
   s.version  = version
   s.summary  = 'Core cross-platform gRPC library, written in C'
   s.homepage = 'http://www.grpc.io'
@@ -44,7 +44,7 @@
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
-    :tag => "objective-c-v#{version}",
+    :tag => "v#{version}",
     # TODO(jcanizales): Depend explicitly on the nanopb pod, and disable submodules.
     :submodules => true,
   }
@@ -144,63 +144,58 @@
                       'include/grpc/support/tls_msvc.h',
                       'include/grpc/support/tls_pthread.h',
                       'include/grpc/support/useful.h',
-                      'include/grpc/impl/codegen/alloc.h',
                       'include/grpc/impl/codegen/atm.h',
                       'include/grpc/impl/codegen/atm_gcc_atomic.h',
                       'include/grpc/impl/codegen/atm_gcc_sync.h',
                       'include/grpc/impl/codegen/atm_windows.h',
-                      'include/grpc/impl/codegen/log.h',
+                      'include/grpc/impl/codegen/gpr_types.h',
                       'include/grpc/impl/codegen/port_platform.h',
                       'include/grpc/impl/codegen/slice.h',
-                      'include/grpc/impl/codegen/slice_buffer.h',
                       'include/grpc/impl/codegen/sync.h',
                       'include/grpc/impl/codegen/sync_generic.h',
                       'include/grpc/impl/codegen/sync_posix.h',
                       'include/grpc/impl/codegen/sync_windows.h',
-                      'include/grpc/impl/codegen/time.h',
                       'include/grpc/byte_buffer.h',
                       'include/grpc/byte_buffer_reader.h',
                       'include/grpc/compression.h',
                       'include/grpc/grpc.h',
                       'include/grpc/grpc_posix.h',
+                      'include/grpc/grpc_security_constants.h',
                       'include/grpc/status.h',
-                      'include/grpc/impl/codegen/byte_buffer.h',
                       'include/grpc/impl/codegen/byte_buffer_reader.h',
                       'include/grpc/impl/codegen/compression_types.h',
                       'include/grpc/impl/codegen/connectivity_state.h',
                       'include/grpc/impl/codegen/grpc_types.h',
                       'include/grpc/impl/codegen/propagation_bits.h',
                       'include/grpc/impl/codegen/status.h',
-                      'include/grpc/impl/codegen/alloc.h',
                       'include/grpc/impl/codegen/atm.h',
                       'include/grpc/impl/codegen/atm_gcc_atomic.h',
                       'include/grpc/impl/codegen/atm_gcc_sync.h',
                       'include/grpc/impl/codegen/atm_windows.h',
-                      'include/grpc/impl/codegen/log.h',
+                      'include/grpc/impl/codegen/gpr_types.h',
                       'include/grpc/impl/codegen/port_platform.h',
                       'include/grpc/impl/codegen/slice.h',
-                      'include/grpc/impl/codegen/slice_buffer.h',
                       'include/grpc/impl/codegen/sync.h',
                       'include/grpc/impl/codegen/sync_generic.h',
                       'include/grpc/impl/codegen/sync_posix.h',
                       'include/grpc/impl/codegen/sync_windows.h',
-                      'include/grpc/impl/codegen/time.h',
                       'include/grpc/grpc_security.h',
-                      'include/grpc/grpc_security_constants.h',
                       'include/grpc/census.h'
   end
   s.subspec 'Implementation' do |ss|
     ss.header_mappings_dir = '.'
     ss.libraries = 'z'
     ss.dependency "#{s.name}/Interface", version
-    ss.dependency 'BoringSSL', '~> 5.0'
+    ss.dependency 'BoringSSL', '~> 6.0'
 
     # To save you from scrolling, this is the last part of the podspec.
     ss.source_files = 'src/core/lib/profiling/timers.h',
                       'src/core/lib/support/backoff.h',
                       'src/core/lib/support/block_annotate.h',
                       'src/core/lib/support/env.h',
+                      'src/core/lib/support/mpscq.h',
                       'src/core/lib/support/murmur_hash.h',
+                      'src/core/lib/support/percent_encoding.h',
                       'src/core/lib/support/stack_lockfree.h',
                       'src/core/lib/support/string.h',
                       'src/core/lib/support/string_windows.h',
@@ -227,7 +222,9 @@
                       'src/core/lib/support/log_linux.c',
                       'src/core/lib/support/log_posix.c',
                       'src/core/lib/support/log_windows.c',
+                      'src/core/lib/support/mpscq.c',
                       'src/core/lib/support/murmur_hash.c',
+                      'src/core/lib/support/percent_encoding.c',
                       'src/core/lib/support/slice.c',
                       'src/core/lib/support/slice_buffer.c',
                       'src/core/lib/support/stack_lockfree.c',
@@ -258,9 +255,11 @@
                       'src/core/lib/channel/compress_filter.h',
                       'src/core/lib/channel/connected_channel.h',
                       'src/core/lib/channel/context.h',
+                      'src/core/lib/channel/deadline_filter.h',
                       'src/core/lib/channel/handshaker.h',
                       'src/core/lib/channel/http_client_filter.h',
                       'src/core/lib/channel/http_server_filter.h',
+                      'src/core/lib/channel/message_size_filter.h',
                       'src/core/lib/compression/algorithm_metadata.h',
                       'src/core/lib/compression/message_compress.h',
                       'src/core/lib/debug/trace.h',
@@ -268,6 +267,7 @@
                       'src/core/lib/http/httpcli.h',
                       'src/core/lib/http/parser.h',
                       'src/core/lib/iomgr/closure.h',
+                      'src/core/lib/iomgr/combiner.h',
                       'src/core/lib/iomgr/endpoint.h',
                       'src/core/lib/iomgr/endpoint_pair.h',
                       'src/core/lib/iomgr/error.h',
@@ -329,6 +329,7 @@
                       'src/core/lib/transport/metadata.h',
                       'src/core/lib/transport/metadata_batch.h',
                       'src/core/lib/transport/static_metadata.h',
+                      'src/core/lib/transport/timeout_encoding.h',
                       'src/core/lib/transport/transport.h',
                       'src/core/lib/transport/transport_impl.h',
                       'src/core/ext/transport/chttp2/transport/bin_decoder.h',
@@ -350,7 +351,6 @@
                       'src/core/ext/transport/chttp2/transport/internal.h',
                       'src/core/ext/transport/chttp2/transport/status_conversion.h',
                       'src/core/ext/transport/chttp2/transport/stream_map.h',
-                      'src/core/ext/transport/chttp2/transport/timeout_encoding.h',
                       'src/core/ext/transport/chttp2/transport/varint.h',
                       'src/core/ext/transport/chttp2/alpn/alpn.h',
                       'src/core/lib/security/context/security_context.h',
@@ -379,8 +379,8 @@
                       'src/core/lib/tsi/transport_security_interface.h',
                       'src/core/ext/client_config/client_channel.h',
                       'src/core/ext/client_config/client_channel_factory.h',
-                      'src/core/ext/client_config/client_config.h',
                       'src/core/ext/client_config/connector.h',
+                      'src/core/ext/client_config/http_connect_handshaker.h',
                       'src/core/ext/client_config/initial_connect_string.h',
                       'src/core/ext/client_config/lb_policy.h',
                       'src/core/ext/client_config/lb_policy_factory.h',
@@ -389,10 +389,11 @@
                       'src/core/ext/client_config/resolver.h',
                       'src/core/ext/client_config/resolver_factory.h',
                       'src/core/ext/client_config/resolver_registry.h',
+                      'src/core/ext/client_config/resolver_result.h',
                       'src/core/ext/client_config/subchannel.h',
-                      'src/core/ext/client_config/subchannel_call_holder.h',
                       'src/core/ext/client_config/subchannel_index.h',
                       'src/core/ext/client_config/uri_parser.h',
+                      'src/core/ext/lb_policy/grpclb/grpclb.h',
                       'src/core/ext/lb_policy/grpclb/load_balancer_api.h',
                       'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h',
                       'third_party/nanopb/pb.h',
@@ -402,21 +403,27 @@
                       'src/core/ext/load_reporting/load_reporting.h',
                       'src/core/ext/load_reporting/load_reporting_filter.h',
                       'src/core/ext/census/aggregation.h',
+                      'src/core/ext/census/base_resources.h',
                       'src/core/ext/census/census_interface.h',
                       'src/core/ext/census/census_rpc_stats.h',
                       'src/core/ext/census/gen/census.pb.h',
+                      'src/core/ext/census/gen/trace_context.pb.h',
                       'src/core/ext/census/grpc_filter.h',
                       'src/core/ext/census/mlog.h',
+                      'src/core/ext/census/resource.h',
                       'src/core/ext/census/rpc_metric_id.h',
+                      'src/core/ext/census/trace_context.h',
                       'src/core/lib/surface/init.c',
                       'src/core/lib/channel/channel_args.c',
                       'src/core/lib/channel/channel_stack.c',
                       'src/core/lib/channel/channel_stack_builder.c',
                       'src/core/lib/channel/compress_filter.c',
                       'src/core/lib/channel/connected_channel.c',
+                      'src/core/lib/channel/deadline_filter.c',
                       'src/core/lib/channel/handshaker.c',
                       'src/core/lib/channel/http_client_filter.c',
                       'src/core/lib/channel/http_server_filter.c',
+                      'src/core/lib/channel/message_size_filter.c',
                       'src/core/lib/compression/compression.c',
                       'src/core/lib/compression/message_compress.c',
                       'src/core/lib/debug/trace.c',
@@ -424,6 +431,7 @@
                       'src/core/lib/http/httpcli.c',
                       'src/core/lib/http/parser.c',
                       'src/core/lib/iomgr/closure.c',
+                      'src/core/lib/iomgr/combiner.c',
                       'src/core/lib/iomgr/endpoint.c',
                       'src/core/lib/iomgr/endpoint_pair_posix.c',
                       'src/core/lib/iomgr/endpoint_pair_windows.c',
@@ -495,6 +503,7 @@
                       'src/core/lib/transport/metadata.c',
                       'src/core/lib/transport/metadata_batch.c',
                       'src/core/lib/transport/static_metadata.c',
+                      'src/core/lib/transport/timeout_encoding.c',
                       'src/core/lib/transport/transport.c',
                       'src/core/lib/transport/transport_op_string.c',
                       'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c',
@@ -517,7 +526,6 @@
                       'src/core/ext/transport/chttp2/transport/status_conversion.c',
                       'src/core/ext/transport/chttp2/transport/stream_lists.c',
                       'src/core/ext/transport/chttp2/transport/stream_map.c',
-                      'src/core/ext/transport/chttp2/transport/timeout_encoding.c',
                       'src/core/ext/transport/chttp2/transport/varint.c',
                       'src/core/ext/transport/chttp2/transport/writing.c',
                       'src/core/ext/transport/chttp2/alpn/alpn.c',
@@ -553,10 +561,10 @@
                       'src/core/ext/client_config/channel_connectivity.c',
                       'src/core/ext/client_config/client_channel.c',
                       'src/core/ext/client_config/client_channel_factory.c',
-                      'src/core/ext/client_config/client_config.c',
                       'src/core/ext/client_config/client_config_plugin.c',
                       'src/core/ext/client_config/connector.c',
                       'src/core/ext/client_config/default_initial_connect_string.c',
+                      'src/core/ext/client_config/http_connect_handshaker.c',
                       'src/core/ext/client_config/initial_connect_string.c',
                       'src/core/ext/client_config/lb_policy.c',
                       'src/core/ext/client_config/lb_policy_factory.c',
@@ -565,14 +573,15 @@
                       'src/core/ext/client_config/resolver.c',
                       'src/core/ext/client_config/resolver_factory.c',
                       'src/core/ext/client_config/resolver_registry.c',
+                      'src/core/ext/client_config/resolver_result.c',
                       'src/core/ext/client_config/subchannel.c',
-                      'src/core/ext/client_config/subchannel_call_holder.c',
                       'src/core/ext/client_config/subchannel_index.c',
                       'src/core/ext/client_config/uri_parser.c',
                       'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c',
                       'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
                       'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
                       'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c',
+                      'src/core/ext/lb_policy/grpclb/grpclb.c',
                       'src/core/ext/lb_policy/grpclb/load_balancer_api.c',
                       'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
                       'third_party/nanopb/pb_common.c',
@@ -584,8 +593,10 @@
                       'src/core/ext/resolver/sockaddr/sockaddr_resolver.c',
                       'src/core/ext/load_reporting/load_reporting.c',
                       'src/core/ext/load_reporting/load_reporting_filter.c',
+                      'src/core/ext/census/base_resources.c',
                       'src/core/ext/census/context.c',
                       'src/core/ext/census/gen/census.pb.c',
+                      'src/core/ext/census/gen/trace_context.pb.c',
                       'src/core/ext/census/grpc_context.c',
                       'src/core/ext/census/grpc_filter.c',
                       'src/core/ext/census/grpc_plugin.c',
@@ -593,6 +604,8 @@
                       'src/core/ext/census/mlog.c',
                       'src/core/ext/census/operation.c',
                       'src/core/ext/census/placeholders.c',
+                      'src/core/ext/census/resource.c',
+                      'src/core/ext/census/trace_context.c',
                       'src/core/ext/census/tracing.c',
                       'src/core/plugin_registry/grpc_plugin_registry.c'
 
@@ -600,7 +613,9 @@
                               'src/core/lib/support/backoff.h',
                               'src/core/lib/support/block_annotate.h',
                               'src/core/lib/support/env.h',
+                              'src/core/lib/support/mpscq.h',
                               'src/core/lib/support/murmur_hash.h',
+                              'src/core/lib/support/percent_encoding.h',
                               'src/core/lib/support/stack_lockfree.h',
                               'src/core/lib/support/string.h',
                               'src/core/lib/support/string_windows.h',
@@ -613,9 +628,11 @@
                               'src/core/lib/channel/compress_filter.h',
                               'src/core/lib/channel/connected_channel.h',
                               'src/core/lib/channel/context.h',
+                              'src/core/lib/channel/deadline_filter.h',
                               'src/core/lib/channel/handshaker.h',
                               'src/core/lib/channel/http_client_filter.h',
                               'src/core/lib/channel/http_server_filter.h',
+                              'src/core/lib/channel/message_size_filter.h',
                               'src/core/lib/compression/algorithm_metadata.h',
                               'src/core/lib/compression/message_compress.h',
                               'src/core/lib/debug/trace.h',
@@ -623,6 +640,7 @@
                               'src/core/lib/http/httpcli.h',
                               'src/core/lib/http/parser.h',
                               'src/core/lib/iomgr/closure.h',
+                              'src/core/lib/iomgr/combiner.h',
                               'src/core/lib/iomgr/endpoint.h',
                               'src/core/lib/iomgr/endpoint_pair.h',
                               'src/core/lib/iomgr/error.h',
@@ -684,6 +702,7 @@
                               'src/core/lib/transport/metadata.h',
                               'src/core/lib/transport/metadata_batch.h',
                               'src/core/lib/transport/static_metadata.h',
+                              'src/core/lib/transport/timeout_encoding.h',
                               'src/core/lib/transport/transport.h',
                               'src/core/lib/transport/transport_impl.h',
                               'src/core/ext/transport/chttp2/transport/bin_decoder.h',
@@ -705,7 +724,6 @@
                               'src/core/ext/transport/chttp2/transport/internal.h',
                               'src/core/ext/transport/chttp2/transport/status_conversion.h',
                               'src/core/ext/transport/chttp2/transport/stream_map.h',
-                              'src/core/ext/transport/chttp2/transport/timeout_encoding.h',
                               'src/core/ext/transport/chttp2/transport/varint.h',
                               'src/core/ext/transport/chttp2/alpn/alpn.h',
                               'src/core/lib/security/context/security_context.h',
@@ -734,8 +752,8 @@
                               'src/core/lib/tsi/transport_security_interface.h',
                               'src/core/ext/client_config/client_channel.h',
                               'src/core/ext/client_config/client_channel_factory.h',
-                              'src/core/ext/client_config/client_config.h',
                               'src/core/ext/client_config/connector.h',
+                              'src/core/ext/client_config/http_connect_handshaker.h',
                               'src/core/ext/client_config/initial_connect_string.h',
                               'src/core/ext/client_config/lb_policy.h',
                               'src/core/ext/client_config/lb_policy_factory.h',
@@ -744,10 +762,11 @@
                               'src/core/ext/client_config/resolver.h',
                               'src/core/ext/client_config/resolver_factory.h',
                               'src/core/ext/client_config/resolver_registry.h',
+                              'src/core/ext/client_config/resolver_result.h',
                               'src/core/ext/client_config/subchannel.h',
-                              'src/core/ext/client_config/subchannel_call_holder.h',
                               'src/core/ext/client_config/subchannel_index.h',
                               'src/core/ext/client_config/uri_parser.h',
+                              'src/core/ext/lb_policy/grpclb/grpclb.h',
                               'src/core/ext/lb_policy/grpclb/load_balancer_api.h',
                               'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h',
                               'third_party/nanopb/pb.h',
@@ -757,12 +776,16 @@
                               'src/core/ext/load_reporting/load_reporting.h',
                               'src/core/ext/load_reporting/load_reporting_filter.h',
                               'src/core/ext/census/aggregation.h',
+                              'src/core/ext/census/base_resources.h',
                               'src/core/ext/census/census_interface.h',
                               'src/core/ext/census/census_rpc_stats.h',
                               'src/core/ext/census/gen/census.pb.h',
+                              'src/core/ext/census/gen/trace_context.pb.h',
                               'src/core/ext/census/grpc_filter.h',
                               'src/core/ext/census/mlog.h',
-                              'src/core/ext/census/rpc_metric_id.h'
+                              'src/core/ext/census/resource.h',
+                              'src/core/ext/census/rpc_metric_id.h',
+                              'src/core/ext/census/trace_context.h'
   end
 
   s.subspec 'Cronet-Interface' do |ss|
@@ -770,12 +793,16 @@
     ss.source_files = 'include/grpc/grpc_cronet.h'
   end
 
-  s.subspec 'Cronet-Tests' do |ss|
+  s.subspec 'Cronet-Implementation' do |ss|
+    ss.header_mappings_dir = '.'
+    ss.source_files = 'src/core/ext/transport/cronet/client/secure/cronet_channel_create.c',
+                      'src/core/ext/transport/cronet/transport/cronet_transport.c'
+  end
+
+  s.subspec 'Tests' do |ss|
     ss.header_mappings_dir = '.'
 
-    ss.source_files = 'src/core/ext/transport/cronet/client/secure/cronet_channel_create.c',
-                      'src/core/ext/transport/cronet/transport/cronet_transport.c',
-                      'test/core/end2end/cq_verifier.{c,h}',
+    ss.source_files = 'test/core/end2end/cq_verifier.{c,h}',
                       'test/core/end2end/end2end_tests.{c,h}',
                       'test/core/end2end/tests/*.{c,h}',
                       'test/core/end2end/data/*.{c,h}',
diff --git a/gRPC-ProtoRPC.podspec b/gRPC-ProtoRPC.podspec
index 741067b..f6426fb 100644
--- a/gRPC-ProtoRPC.podspec
+++ b/gRPC-ProtoRPC.podspec
@@ -1,9 +1,3 @@
-# GRPC CocoaPods podspec
-# This file has been automatically generated from a template file.
-# Please look at the templates directory instead.
-# This file can be regenerated from the template by running
-# tools/buildgen/generate_projects.sh
-
 # Copyright 2015, Google Inc.
 # All rights reserved.
 #
@@ -36,7 +30,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-ProtoRPC'
-  version = '1.0.0-pre1'
+  version = '1.0.0'
   s.version  = version
   s.summary  = 'RPC library for Protocol Buffers, based on gRPC'
   s.homepage = 'http://www.grpc.io'
@@ -45,7 +39,7 @@
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
-    :tag => "objective-c-v#{version}",
+    :tag => "v#{version}",
   }
 
   s.ios.deployment_target = '7.1'
@@ -61,9 +55,11 @@
 
   s.dependency 'gRPC', version
   s.dependency 'gRPC-RxLibrary', version
-  s.dependency 'Protobuf', '~> 3.0.0-beta-3.1'
-  # This is needed by all pods that depend on Protobuf:
+  s.dependency 'Protobuf', '~> 3.0'
   s.pod_target_xcconfig = {
+    # This is needed by all pods that depend on Protobuf:
     'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
+    # This is needed by all pods that depend on gRPC-RxLibrary:
+    'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
   }
 end
diff --git a/gRPC-RxLibrary.podspec b/gRPC-RxLibrary.podspec
index 862ed89..3168434 100644
--- a/gRPC-RxLibrary.podspec
+++ b/gRPC-RxLibrary.podspec
@@ -1,9 +1,3 @@
-# GRPC CocoaPods podspec
-# This file has been automatically generated from a template file.
-# Please look at the templates directory instead.
-# This file can be regenerated from the template by running
-# tools/buildgen/generate_projects.sh
-
 # Copyright 2015, Google Inc.
 # All rights reserved.
 #
@@ -36,7 +30,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC-RxLibrary'
-  version = '1.0.0-pre1'
+  version = '1.0.0'
   s.version  = version
   s.summary  = 'Reactive Extensions library for iOS/OSX.'
   s.homepage = 'http://www.grpc.io'
@@ -45,7 +39,7 @@
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
-    :tag => "objective-c-v#{version}",
+    :tag => "v#{version}",
   }
 
   s.ios.deployment_target = '7.1'
diff --git a/gRPC.podspec b/gRPC.podspec
index eda965a..9a47934 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -1,9 +1,3 @@
-# GRPC CocoaPods podspec
-# This file has been automatically generated from a template file.
-# Please look at the templates directory instead.
-# This file can be regenerated from the template by running
-# tools/buildgen/generate_projects.sh
-
 # Copyright 2015, Google Inc.
 # All rights reserved.
 #
@@ -36,7 +30,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'gRPC'
-  version = '1.0.0-pre1'
+  version = '1.0.0'
   s.version  = version
   s.summary  = 'gRPC client library for iOS/OSX'
   s.homepage = 'http://www.grpc.io'
@@ -45,7 +39,7 @@
 
   s.source = {
     :git => 'https://github.com/grpc/grpc.git',
-    :tag => "objective-c-v#{version}",
+    :tag => "v#{version}",
   }
 
   s.ios.deployment_target = '7.1'
@@ -65,4 +59,9 @@
 
   # Certificates, to be able to establish TLS connections:
   s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] }
+
+  s.pod_target_xcconfig = {
+    # This is needed by all pods that depend on gRPC-RxLibrary:
+    'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
+  }
 end
diff --git a/grpc.def b/grpc.def
index c2f83f5..6e905a2 100644
--- a/grpc.def
+++ b/grpc.def
@@ -1,4 +1,14 @@
 EXPORTS
+    grpc_raw_byte_buffer_create
+    grpc_raw_compressed_byte_buffer_create
+    grpc_byte_buffer_copy
+    grpc_byte_buffer_length
+    grpc_byte_buffer_destroy
+    grpc_byte_buffer_reader_init
+    grpc_byte_buffer_reader_destroy
+    grpc_byte_buffer_reader_next
+    grpc_byte_buffer_reader_readall
+    grpc_raw_byte_buffer_from_reader
     census_initialize
     census_shutdown
     census_supported
@@ -23,15 +33,10 @@
     census_trace_scan_start
     census_get_trace_record
     census_trace_scan_end
+    census_define_resource
+    census_delete_resource
+    census_resource_id
     census_record_values
-    census_view_create
-    census_view_delete
-    census_view_metric
-    census_view_naggregations
-    census_view_tags
-    census_view_aggregrations
-    census_view_get_data
-    census_view_reset
     grpc_compression_algorithm_parse
     grpc_compression_algorithm_name
     grpc_compression_algorithm_for_level
@@ -47,6 +52,7 @@
     grpc_init
     grpc_shutdown
     grpc_version_string
+    grpc_g_stands_for
     grpc_completion_queue_create
     grpc_completion_queue_next
     grpc_completion_queue_pluck
@@ -130,94 +136,6 @@
     gpr_free_aligned
     gpr_set_allocation_functions
     gpr_get_allocation_functions
-    grpc_raw_byte_buffer_create
-    grpc_raw_compressed_byte_buffer_create
-    grpc_byte_buffer_copy
-    grpc_byte_buffer_length
-    grpc_byte_buffer_destroy
-    grpc_byte_buffer_reader_init
-    grpc_byte_buffer_reader_destroy
-    grpc_byte_buffer_reader_next
-    grpc_byte_buffer_reader_readall
-    grpc_raw_byte_buffer_from_reader
-    gpr_log
-    gpr_log_message
-    gpr_set_log_verbosity
-    gpr_log_verbosity_init
-    gpr_set_log_function
-    gpr_slice_ref
-    gpr_slice_unref
-    gpr_slice_new
-    gpr_slice_new_with_user_data
-    gpr_slice_new_with_len
-    gpr_slice_malloc
-    gpr_slice_from_copied_string
-    gpr_slice_from_copied_buffer
-    gpr_slice_from_static_string
-    gpr_slice_sub
-    gpr_slice_sub_no_ref
-    gpr_slice_split_tail
-    gpr_slice_split_head
-    gpr_empty_slice
-    gpr_slice_cmp
-    gpr_slice_str_cmp
-    gpr_slice_buffer_init
-    gpr_slice_buffer_destroy
-    gpr_slice_buffer_add
-    gpr_slice_buffer_add_indexed
-    gpr_slice_buffer_addn
-    gpr_slice_buffer_tiny_add
-    gpr_slice_buffer_pop
-    gpr_slice_buffer_reset_and_unref
-    gpr_slice_buffer_swap
-    gpr_slice_buffer_move_into
-    gpr_slice_buffer_trim_end
-    gpr_slice_buffer_move_first
-    gpr_slice_buffer_take_first
-    gpr_mu_init
-    gpr_mu_destroy
-    gpr_mu_lock
-    gpr_mu_unlock
-    gpr_mu_trylock
-    gpr_cv_init
-    gpr_cv_destroy
-    gpr_cv_wait
-    gpr_cv_signal
-    gpr_cv_broadcast
-    gpr_once_init
-    gpr_event_init
-    gpr_event_set
-    gpr_event_get
-    gpr_event_wait
-    gpr_ref_init
-    gpr_ref
-    gpr_ref_non_zero
-    gpr_refn
-    gpr_unref
-    gpr_stats_init
-    gpr_stats_inc
-    gpr_stats_read
-    gpr_time_0
-    gpr_inf_future
-    gpr_inf_past
-    gpr_time_init
-    gpr_now
-    gpr_convert_clock_type
-    gpr_time_cmp
-    gpr_time_max
-    gpr_time_min
-    gpr_time_add
-    gpr_time_sub
-    gpr_time_from_micros
-    gpr_time_from_nanos
-    gpr_time_from_millis
-    gpr_time_from_seconds
-    gpr_time_from_minutes
-    gpr_time_from_hours
-    gpr_time_to_millis
-    gpr_time_similar
-    gpr_sleep_until
-    gpr_timespec_to_micros
     gpr_avl_create
     gpr_avl_ref
     gpr_avl_unref
@@ -254,7 +172,41 @@
     gpr_histogram_merge_contents
     gpr_join_host_port
     gpr_split_host_port
+    gpr_log
+    gpr_log_message
+    gpr_set_log_verbosity
+    gpr_log_verbosity_init
+    gpr_set_log_function
     gpr_format_message
+    gpr_slice_ref
+    gpr_slice_unref
+    gpr_slice_new
+    gpr_slice_new_with_user_data
+    gpr_slice_new_with_len
+    gpr_slice_malloc
+    gpr_slice_from_copied_string
+    gpr_slice_from_copied_buffer
+    gpr_slice_from_static_string
+    gpr_slice_sub
+    gpr_slice_sub_no_ref
+    gpr_slice_split_tail
+    gpr_slice_split_head
+    gpr_empty_slice
+    gpr_slice_cmp
+    gpr_slice_str_cmp
+    gpr_slice_buffer_init
+    gpr_slice_buffer_destroy
+    gpr_slice_buffer_add
+    gpr_slice_buffer_add_indexed
+    gpr_slice_buffer_addn
+    gpr_slice_buffer_tiny_add
+    gpr_slice_buffer_pop
+    gpr_slice_buffer_reset_and_unref
+    gpr_slice_buffer_swap
+    gpr_slice_buffer_move_into
+    gpr_slice_buffer_trim_end
+    gpr_slice_buffer_move_first
+    gpr_slice_buffer_take_first
     gpr_strdup
     gpr_asprintf
     gpr_subprocess_binary_extension
@@ -262,6 +214,29 @@
     gpr_subprocess_destroy
     gpr_subprocess_join
     gpr_subprocess_interrupt
+    gpr_mu_init
+    gpr_mu_destroy
+    gpr_mu_lock
+    gpr_mu_unlock
+    gpr_mu_trylock
+    gpr_cv_init
+    gpr_cv_destroy
+    gpr_cv_wait
+    gpr_cv_signal
+    gpr_cv_broadcast
+    gpr_once_init
+    gpr_event_init
+    gpr_event_set
+    gpr_event_get
+    gpr_event_wait
+    gpr_ref_init
+    gpr_ref
+    gpr_ref_non_zero
+    gpr_refn
+    gpr_unref
+    gpr_stats_init
+    gpr_stats_inc
+    gpr_stats_read
     gpr_thd_new
     gpr_thd_options_default
     gpr_thd_options_set_detached
@@ -270,3 +245,24 @@
     gpr_thd_options_is_joinable
     gpr_thd_currentid
     gpr_thd_join
+    gpr_time_0
+    gpr_inf_future
+    gpr_inf_past
+    gpr_time_init
+    gpr_now
+    gpr_convert_clock_type
+    gpr_time_cmp
+    gpr_time_max
+    gpr_time_min
+    gpr_time_add
+    gpr_time_sub
+    gpr_time_from_micros
+    gpr_time_from_nanos
+    gpr_time_from_millis
+    gpr_time_from_seconds
+    gpr_time_from_minutes
+    gpr_time_from_hours
+    gpr_time_to_millis
+    gpr_time_similar
+    gpr_sleep_until
+    gpr_timespec_to_micros
diff --git a/grpc.gemspec b/grpc.gemspec
index 6b8beba..6079ea2 100755
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -27,8 +27,9 @@
   s.require_paths = %w( src/ruby/bin src/ruby/lib src/ruby/pb )
   s.platform      = Gem::Platform::RUBY
 
-  s.add_dependency 'google-protobuf', '~> 3.0.0.alpha.5.0.3'
+  s.add_dependency 'google-protobuf', '~> 3.0.2'
   s.add_dependency 'googleauth',      '~> 0.5.1'
+  s.add_dependency 'concurrent-ruby'
 
   s.add_development_dependency 'bundler',            '~> 1.9'
   s.add_development_dependency 'facter',             '~> 2.4'
@@ -71,25 +72,24 @@
   s.files += %w( include/grpc/support/tls_msvc.h )
   s.files += %w( include/grpc/support/tls_pthread.h )
   s.files += %w( include/grpc/support/useful.h )
-  s.files += %w( include/grpc/impl/codegen/alloc.h )
   s.files += %w( include/grpc/impl/codegen/atm.h )
   s.files += %w( include/grpc/impl/codegen/atm_gcc_atomic.h )
   s.files += %w( include/grpc/impl/codegen/atm_gcc_sync.h )
   s.files += %w( include/grpc/impl/codegen/atm_windows.h )
-  s.files += %w( include/grpc/impl/codegen/log.h )
+  s.files += %w( include/grpc/impl/codegen/gpr_types.h )
   s.files += %w( include/grpc/impl/codegen/port_platform.h )
   s.files += %w( include/grpc/impl/codegen/slice.h )
-  s.files += %w( include/grpc/impl/codegen/slice_buffer.h )
   s.files += %w( include/grpc/impl/codegen/sync.h )
   s.files += %w( include/grpc/impl/codegen/sync_generic.h )
   s.files += %w( include/grpc/impl/codegen/sync_posix.h )
   s.files += %w( include/grpc/impl/codegen/sync_windows.h )
-  s.files += %w( include/grpc/impl/codegen/time.h )
   s.files += %w( src/core/lib/profiling/timers.h )
   s.files += %w( src/core/lib/support/backoff.h )
   s.files += %w( src/core/lib/support/block_annotate.h )
   s.files += %w( src/core/lib/support/env.h )
+  s.files += %w( src/core/lib/support/mpscq.h )
   s.files += %w( src/core/lib/support/murmur_hash.h )
+  s.files += %w( src/core/lib/support/percent_encoding.h )
   s.files += %w( src/core/lib/support/stack_lockfree.h )
   s.files += %w( src/core/lib/support/string.h )
   s.files += %w( src/core/lib/support/string_windows.h )
@@ -116,7 +116,9 @@
   s.files += %w( src/core/lib/support/log_linux.c )
   s.files += %w( src/core/lib/support/log_posix.c )
   s.files += %w( src/core/lib/support/log_windows.c )
+  s.files += %w( src/core/lib/support/mpscq.c )
   s.files += %w( src/core/lib/support/murmur_hash.c )
+  s.files += %w( src/core/lib/support/percent_encoding.c )
   s.files += %w( src/core/lib/support/slice.c )
   s.files += %w( src/core/lib/support/slice_buffer.c )
   s.files += %w( src/core/lib/support/stack_lockfree.c )
@@ -146,30 +148,26 @@
   s.files += %w( include/grpc/compression.h )
   s.files += %w( include/grpc/grpc.h )
   s.files += %w( include/grpc/grpc_posix.h )
+  s.files += %w( include/grpc/grpc_security_constants.h )
   s.files += %w( include/grpc/status.h )
-  s.files += %w( include/grpc/impl/codegen/byte_buffer.h )
   s.files += %w( include/grpc/impl/codegen/byte_buffer_reader.h )
   s.files += %w( include/grpc/impl/codegen/compression_types.h )
   s.files += %w( include/grpc/impl/codegen/connectivity_state.h )
   s.files += %w( include/grpc/impl/codegen/grpc_types.h )
   s.files += %w( include/grpc/impl/codegen/propagation_bits.h )
   s.files += %w( include/grpc/impl/codegen/status.h )
-  s.files += %w( include/grpc/impl/codegen/alloc.h )
   s.files += %w( include/grpc/impl/codegen/atm.h )
   s.files += %w( include/grpc/impl/codegen/atm_gcc_atomic.h )
   s.files += %w( include/grpc/impl/codegen/atm_gcc_sync.h )
   s.files += %w( include/grpc/impl/codegen/atm_windows.h )
-  s.files += %w( include/grpc/impl/codegen/log.h )
+  s.files += %w( include/grpc/impl/codegen/gpr_types.h )
   s.files += %w( include/grpc/impl/codegen/port_platform.h )
   s.files += %w( include/grpc/impl/codegen/slice.h )
-  s.files += %w( include/grpc/impl/codegen/slice_buffer.h )
   s.files += %w( include/grpc/impl/codegen/sync.h )
   s.files += %w( include/grpc/impl/codegen/sync_generic.h )
   s.files += %w( include/grpc/impl/codegen/sync_posix.h )
   s.files += %w( include/grpc/impl/codegen/sync_windows.h )
-  s.files += %w( include/grpc/impl/codegen/time.h )
   s.files += %w( include/grpc/grpc_security.h )
-  s.files += %w( include/grpc/grpc_security_constants.h )
   s.files += %w( include/grpc/census.h )
   s.files += %w( src/core/lib/channel/channel_args.h )
   s.files += %w( src/core/lib/channel/channel_stack.h )
@@ -177,9 +175,11 @@
   s.files += %w( src/core/lib/channel/compress_filter.h )
   s.files += %w( src/core/lib/channel/connected_channel.h )
   s.files += %w( src/core/lib/channel/context.h )
+  s.files += %w( src/core/lib/channel/deadline_filter.h )
   s.files += %w( src/core/lib/channel/handshaker.h )
   s.files += %w( src/core/lib/channel/http_client_filter.h )
   s.files += %w( src/core/lib/channel/http_server_filter.h )
+  s.files += %w( src/core/lib/channel/message_size_filter.h )
   s.files += %w( src/core/lib/compression/algorithm_metadata.h )
   s.files += %w( src/core/lib/compression/message_compress.h )
   s.files += %w( src/core/lib/debug/trace.h )
@@ -187,6 +187,7 @@
   s.files += %w( src/core/lib/http/httpcli.h )
   s.files += %w( src/core/lib/http/parser.h )
   s.files += %w( src/core/lib/iomgr/closure.h )
+  s.files += %w( src/core/lib/iomgr/combiner.h )
   s.files += %w( src/core/lib/iomgr/endpoint.h )
   s.files += %w( src/core/lib/iomgr/endpoint_pair.h )
   s.files += %w( src/core/lib/iomgr/error.h )
@@ -248,6 +249,7 @@
   s.files += %w( src/core/lib/transport/metadata.h )
   s.files += %w( src/core/lib/transport/metadata_batch.h )
   s.files += %w( src/core/lib/transport/static_metadata.h )
+  s.files += %w( src/core/lib/transport/timeout_encoding.h )
   s.files += %w( src/core/lib/transport/transport.h )
   s.files += %w( src/core/lib/transport/transport_impl.h )
   s.files += %w( src/core/ext/transport/chttp2/transport/bin_decoder.h )
@@ -269,7 +271,6 @@
   s.files += %w( src/core/ext/transport/chttp2/transport/internal.h )
   s.files += %w( src/core/ext/transport/chttp2/transport/status_conversion.h )
   s.files += %w( src/core/ext/transport/chttp2/transport/stream_map.h )
-  s.files += %w( src/core/ext/transport/chttp2/transport/timeout_encoding.h )
   s.files += %w( src/core/ext/transport/chttp2/transport/varint.h )
   s.files += %w( src/core/ext/transport/chttp2/alpn/alpn.h )
   s.files += %w( src/core/lib/security/context/security_context.h )
@@ -298,8 +299,8 @@
   s.files += %w( src/core/lib/tsi/transport_security_interface.h )
   s.files += %w( src/core/ext/client_config/client_channel.h )
   s.files += %w( src/core/ext/client_config/client_channel_factory.h )
-  s.files += %w( src/core/ext/client_config/client_config.h )
   s.files += %w( src/core/ext/client_config/connector.h )
+  s.files += %w( src/core/ext/client_config/http_connect_handshaker.h )
   s.files += %w( src/core/ext/client_config/initial_connect_string.h )
   s.files += %w( src/core/ext/client_config/lb_policy.h )
   s.files += %w( src/core/ext/client_config/lb_policy_factory.h )
@@ -308,10 +309,11 @@
   s.files += %w( src/core/ext/client_config/resolver.h )
   s.files += %w( src/core/ext/client_config/resolver_factory.h )
   s.files += %w( src/core/ext/client_config/resolver_registry.h )
+  s.files += %w( src/core/ext/client_config/resolver_result.h )
   s.files += %w( src/core/ext/client_config/subchannel.h )
-  s.files += %w( src/core/ext/client_config/subchannel_call_holder.h )
   s.files += %w( src/core/ext/client_config/subchannel_index.h )
   s.files += %w( src/core/ext/client_config/uri_parser.h )
+  s.files += %w( src/core/ext/lb_policy/grpclb/grpclb.h )
   s.files += %w( src/core/ext/lb_policy/grpclb/load_balancer_api.h )
   s.files += %w( src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h )
   s.files += %w( third_party/nanopb/pb.h )
@@ -321,21 +323,27 @@
   s.files += %w( src/core/ext/load_reporting/load_reporting.h )
   s.files += %w( src/core/ext/load_reporting/load_reporting_filter.h )
   s.files += %w( src/core/ext/census/aggregation.h )
+  s.files += %w( src/core/ext/census/base_resources.h )
   s.files += %w( src/core/ext/census/census_interface.h )
   s.files += %w( src/core/ext/census/census_rpc_stats.h )
   s.files += %w( src/core/ext/census/gen/census.pb.h )
+  s.files += %w( src/core/ext/census/gen/trace_context.pb.h )
   s.files += %w( src/core/ext/census/grpc_filter.h )
   s.files += %w( src/core/ext/census/mlog.h )
+  s.files += %w( src/core/ext/census/resource.h )
   s.files += %w( src/core/ext/census/rpc_metric_id.h )
+  s.files += %w( src/core/ext/census/trace_context.h )
   s.files += %w( src/core/lib/surface/init.c )
   s.files += %w( src/core/lib/channel/channel_args.c )
   s.files += %w( src/core/lib/channel/channel_stack.c )
   s.files += %w( src/core/lib/channel/channel_stack_builder.c )
   s.files += %w( src/core/lib/channel/compress_filter.c )
   s.files += %w( src/core/lib/channel/connected_channel.c )
+  s.files += %w( src/core/lib/channel/deadline_filter.c )
   s.files += %w( src/core/lib/channel/handshaker.c )
   s.files += %w( src/core/lib/channel/http_client_filter.c )
   s.files += %w( src/core/lib/channel/http_server_filter.c )
+  s.files += %w( src/core/lib/channel/message_size_filter.c )
   s.files += %w( src/core/lib/compression/compression.c )
   s.files += %w( src/core/lib/compression/message_compress.c )
   s.files += %w( src/core/lib/debug/trace.c )
@@ -343,6 +351,7 @@
   s.files += %w( src/core/lib/http/httpcli.c )
   s.files += %w( src/core/lib/http/parser.c )
   s.files += %w( src/core/lib/iomgr/closure.c )
+  s.files += %w( src/core/lib/iomgr/combiner.c )
   s.files += %w( src/core/lib/iomgr/endpoint.c )
   s.files += %w( src/core/lib/iomgr/endpoint_pair_posix.c )
   s.files += %w( src/core/lib/iomgr/endpoint_pair_windows.c )
@@ -414,6 +423,7 @@
   s.files += %w( src/core/lib/transport/metadata.c )
   s.files += %w( src/core/lib/transport/metadata_batch.c )
   s.files += %w( src/core/lib/transport/static_metadata.c )
+  s.files += %w( src/core/lib/transport/timeout_encoding.c )
   s.files += %w( src/core/lib/transport/transport.c )
   s.files += %w( src/core/lib/transport/transport_op_string.c )
   s.files += %w( src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c )
@@ -436,7 +446,6 @@
   s.files += %w( src/core/ext/transport/chttp2/transport/status_conversion.c )
   s.files += %w( src/core/ext/transport/chttp2/transport/stream_lists.c )
   s.files += %w( src/core/ext/transport/chttp2/transport/stream_map.c )
-  s.files += %w( src/core/ext/transport/chttp2/transport/timeout_encoding.c )
   s.files += %w( src/core/ext/transport/chttp2/transport/varint.c )
   s.files += %w( src/core/ext/transport/chttp2/transport/writing.c )
   s.files += %w( src/core/ext/transport/chttp2/alpn/alpn.c )
@@ -472,10 +481,10 @@
   s.files += %w( src/core/ext/client_config/channel_connectivity.c )
   s.files += %w( src/core/ext/client_config/client_channel.c )
   s.files += %w( src/core/ext/client_config/client_channel_factory.c )
-  s.files += %w( src/core/ext/client_config/client_config.c )
   s.files += %w( src/core/ext/client_config/client_config_plugin.c )
   s.files += %w( src/core/ext/client_config/connector.c )
   s.files += %w( src/core/ext/client_config/default_initial_connect_string.c )
+  s.files += %w( src/core/ext/client_config/http_connect_handshaker.c )
   s.files += %w( src/core/ext/client_config/initial_connect_string.c )
   s.files += %w( src/core/ext/client_config/lb_policy.c )
   s.files += %w( src/core/ext/client_config/lb_policy_factory.c )
@@ -484,14 +493,15 @@
   s.files += %w( src/core/ext/client_config/resolver.c )
   s.files += %w( src/core/ext/client_config/resolver_factory.c )
   s.files += %w( src/core/ext/client_config/resolver_registry.c )
+  s.files += %w( src/core/ext/client_config/resolver_result.c )
   s.files += %w( src/core/ext/client_config/subchannel.c )
-  s.files += %w( src/core/ext/client_config/subchannel_call_holder.c )
   s.files += %w( src/core/ext/client_config/subchannel_index.c )
   s.files += %w( src/core/ext/client_config/uri_parser.c )
   s.files += %w( src/core/ext/transport/chttp2/server/insecure/server_chttp2.c )
   s.files += %w( src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c )
   s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create.c )
   s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c )
+  s.files += %w( src/core/ext/lb_policy/grpclb/grpclb.c )
   s.files += %w( src/core/ext/lb_policy/grpclb/load_balancer_api.c )
   s.files += %w( src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c )
   s.files += %w( third_party/nanopb/pb_common.c )
@@ -503,8 +513,10 @@
   s.files += %w( src/core/ext/resolver/sockaddr/sockaddr_resolver.c )
   s.files += %w( src/core/ext/load_reporting/load_reporting.c )
   s.files += %w( src/core/ext/load_reporting/load_reporting_filter.c )
+  s.files += %w( src/core/ext/census/base_resources.c )
   s.files += %w( src/core/ext/census/context.c )
   s.files += %w( src/core/ext/census/gen/census.pb.c )
+  s.files += %w( src/core/ext/census/gen/trace_context.pb.c )
   s.files += %w( src/core/ext/census/grpc_context.c )
   s.files += %w( src/core/ext/census/grpc_filter.c )
   s.files += %w( src/core/ext/census/grpc_plugin.c )
@@ -512,6 +524,8 @@
   s.files += %w( src/core/ext/census/mlog.c )
   s.files += %w( src/core/ext/census/operation.c )
   s.files += %w( src/core/ext/census/placeholders.c )
+  s.files += %w( src/core/ext/census/resource.c )
+  s.files += %w( src/core/ext/census/trace_context.c )
   s.files += %w( src/core/ext/census/tracing.c )
   s.files += %w( src/core/plugin_registry/grpc_plugin_registry.c )
   s.files += %w( third_party/boringssl/crypto/aes/internal.h )
diff --git a/include/grpc++/create_channel.h b/include/grpc++/create_channel.h
index e9ccb51..0537695 100644
--- a/include/grpc++/create_channel.h
+++ b/include/grpc++/create_channel.h
@@ -48,7 +48,6 @@
 /// \param target The URI of the endpoint to connect to.
 /// \param creds Credentials to use for the created channel. If it does not hold
 /// an object or is invalid, a lame channel is returned.
-/// \param args Options for channel creation.
 std::shared_ptr<Channel> CreateChannel(
     const grpc::string& target,
     const std::shared_ptr<ChannelCredentials>& creds);
diff --git a/include/grpc++/create_channel_posix.h b/include/grpc++/create_channel_posix.h
index 0d96159..2af12e6 100644
--- a/include/grpc++/create_channel_posix.h
+++ b/include/grpc++/create_channel_posix.h
@@ -37,6 +37,7 @@
 #include <memory>
 
 #include <grpc++/channel.h>
+#include <grpc++/support/channel_arguments.h>
 #include <grpc/support/port_platform.h>
 
 namespace grpc {
@@ -50,6 +51,15 @@
 std::shared_ptr<Channel> CreateInsecureChannelFromFd(const grpc::string& target,
                                                      int fd);
 
+/// Create a new \a Channel communicating over given file descriptor with custom
+/// channel arguments
+///
+/// \param target The name of the target.
+/// \param fd The file descriptor representing a socket.
+/// \param args Options for channel creation.
+std::shared_ptr<Channel> CreateCustomInsecureChannelFromFd(
+    const grpc::string& target, int fd, const ChannelArguments& args);
+
 #endif  // GPR_SUPPORT_CHANNELS_FROM_FD
 
 }  // namespace grpc
diff --git a/include/grpc++/ext/reflection.grpc.pb.h b/include/grpc++/ext/reflection.grpc.pb.h
index 0b4ef86..6e56088 100644
--- a/include/grpc++/ext/reflection.grpc.pb.h
+++ b/include/grpc++/ext/reflection.grpc.pb.h
@@ -32,7 +32,7 @@
  */
 
 
-// Generated by the gRPC protobuf plugin.
+// Generated by tools/codegen/extensions/gen_reflection_proto.sh
 // If you make any local change, they will be lost.
 // source: reflection.proto
 // Original file comments:
@@ -74,6 +74,7 @@
 
 #include <grpc++/impl/codegen/async_stream.h>
 #include <grpc++/impl/codegen/async_unary_call.h>
+#include <grpc++/impl/codegen/method_handler_impl.h>
 #include <grpc++/impl/codegen/proto_utils.h>
 #include <grpc++/impl/codegen/rpc_method.h>
 #include <grpc++/impl/codegen/service_type.h>
@@ -174,6 +175,7 @@
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
   };
+  typedef Service StreamedUnaryService;
 };
 
 }  // namespace v1alpha
diff --git a/include/grpc++/ext/reflection.pb.h b/include/grpc++/ext/reflection.pb.h
index 00d0773..6e645af 100644
--- a/include/grpc++/ext/reflection.pb.h
+++ b/include/grpc++/ext/reflection.pb.h
@@ -32,7 +32,7 @@
  */
 
 
-// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// Generated by tools/codegen/extensions/gen_reflection_proto.sh
 // source: reflection.proto
 
 #ifndef PROTOBUF_reflection_2eproto__INCLUDED
@@ -42,12 +42,12 @@
 
 #include <google/protobuf/stubs/common.h>
 
-#if GOOGLE_PROTOBUF_VERSION < 3000000
+#if GOOGLE_PROTOBUF_VERSION < 3001000
 #error This file was generated by a newer version of protoc which is
 #error incompatible with your Protocol Buffer headers.  Please update
 #error your headers.
 #endif
-#if 3000000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
+#if 3001000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
 #error This file was generated by an older version of protoc which is
 #error incompatible with your Protocol Buffer headers.  Please
 #error regenerate this file with a newer version of protoc.
@@ -69,6 +69,7 @@
 
 // Internal implementation detail -- do not call these.
 void protobuf_AddDesc_reflection_2eproto();
+void protobuf_InitDefaults_reflection_2eproto();
 void protobuf_AssignDesc_reflection_2eproto();
 void protobuf_ShutdownFile_reflection_2eproto();
 
@@ -83,7 +84,7 @@
 
 // ===================================================================
 
-class ServerReflectionRequest : public ::google::protobuf::Message {
+class ServerReflectionRequest : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:grpc.reflection.v1alpha.ServerReflectionRequest) */ {
  public:
   ServerReflectionRequest();
   virtual ~ServerReflectionRequest();
@@ -107,6 +108,8 @@
     MESSAGE_REQUEST_NOT_SET = 0,
   };
 
+  static const ServerReflectionRequest* internal_default_instance();
+
   void Swap(ServerReflectionRequest* other);
 
   // implements Message ----------------------------------------------
@@ -121,18 +124,23 @@
   void Clear();
   bool IsInitialized() const;
 
-  int ByteSize() const;
+  size_t ByteSizeLong() const;
   bool MergePartialFromCodedStream(
       ::google::protobuf::io::CodedInputStream* input);
   void SerializeWithCachedSizes(
       ::google::protobuf::io::CodedOutputStream* output) const;
-  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
+      bool deterministic, ::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {
+    return InternalSerializeWithCachedSizesToArray(false, output);
+  }
   int GetCachedSize() const { return _cached_size_; }
   private:
   void SharedCtor();
   void SharedDtor();
   void SetCachedSize(int size) const;
   void InternalSwap(ServerReflectionRequest* other);
+  void UnsafeMergeFrom(const ServerReflectionRequest& from);
   private:
   inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
     return _internal_metadata_.arena();
@@ -238,7 +246,6 @@
   inline void clear_has_message_request();
 
   ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
-  bool _is_default_instance_;
   ::google::protobuf::internal::ArenaStringPtr host_;
   union MessageRequestUnion {
     MessageRequestUnion() {}
@@ -251,16 +258,18 @@
   mutable int _cached_size_;
   ::google::protobuf::uint32 _oneof_case_[1];
 
-  friend void  protobuf_AddDesc_reflection_2eproto();
+  friend void  protobuf_InitDefaults_reflection_2eproto_impl();
+  friend void  protobuf_AddDesc_reflection_2eproto_impl();
   friend void protobuf_AssignDesc_reflection_2eproto();
   friend void protobuf_ShutdownFile_reflection_2eproto();
 
   void InitAsDefaultInstance();
-  static ServerReflectionRequest* default_instance_;
 };
+extern ::google::protobuf::internal::ExplicitlyConstructed<ServerReflectionRequest> ServerReflectionRequest_default_instance_;
+
 // -------------------------------------------------------------------
 
-class ExtensionRequest : public ::google::protobuf::Message {
+class ExtensionRequest : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:grpc.reflection.v1alpha.ExtensionRequest) */ {
  public:
   ExtensionRequest();
   virtual ~ExtensionRequest();
@@ -275,6 +284,8 @@
   static const ::google::protobuf::Descriptor* descriptor();
   static const ExtensionRequest& default_instance();
 
+  static const ExtensionRequest* internal_default_instance();
+
   void Swap(ExtensionRequest* other);
 
   // implements Message ----------------------------------------------
@@ -289,18 +300,23 @@
   void Clear();
   bool IsInitialized() const;
 
-  int ByteSize() const;
+  size_t ByteSizeLong() const;
   bool MergePartialFromCodedStream(
       ::google::protobuf::io::CodedInputStream* input);
   void SerializeWithCachedSizes(
       ::google::protobuf::io::CodedOutputStream* output) const;
-  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
+      bool deterministic, ::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {
+    return InternalSerializeWithCachedSizesToArray(false, output);
+  }
   int GetCachedSize() const { return _cached_size_; }
   private:
   void SharedCtor();
   void SharedDtor();
   void SetCachedSize(int size) const;
   void InternalSwap(ExtensionRequest* other);
+  void UnsafeMergeFrom(const ExtensionRequest& from);
   private:
   inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
     return _internal_metadata_.arena();
@@ -337,20 +353,21 @@
  private:
 
   ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
-  bool _is_default_instance_;
   ::google::protobuf::internal::ArenaStringPtr containing_type_;
   ::google::protobuf::int32 extension_number_;
   mutable int _cached_size_;
-  friend void  protobuf_AddDesc_reflection_2eproto();
+  friend void  protobuf_InitDefaults_reflection_2eproto_impl();
+  friend void  protobuf_AddDesc_reflection_2eproto_impl();
   friend void protobuf_AssignDesc_reflection_2eproto();
   friend void protobuf_ShutdownFile_reflection_2eproto();
 
   void InitAsDefaultInstance();
-  static ExtensionRequest* default_instance_;
 };
+extern ::google::protobuf::internal::ExplicitlyConstructed<ExtensionRequest> ExtensionRequest_default_instance_;
+
 // -------------------------------------------------------------------
 
-class ServerReflectionResponse : public ::google::protobuf::Message {
+class ServerReflectionResponse : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:grpc.reflection.v1alpha.ServerReflectionResponse) */ {
  public:
   ServerReflectionResponse();
   virtual ~ServerReflectionResponse();
@@ -373,6 +390,8 @@
     MESSAGE_RESPONSE_NOT_SET = 0,
   };
 
+  static const ServerReflectionResponse* internal_default_instance();
+
   void Swap(ServerReflectionResponse* other);
 
   // implements Message ----------------------------------------------
@@ -387,18 +406,23 @@
   void Clear();
   bool IsInitialized() const;
 
-  int ByteSize() const;
+  size_t ByteSizeLong() const;
   bool MergePartialFromCodedStream(
       ::google::protobuf::io::CodedInputStream* input);
   void SerializeWithCachedSizes(
       ::google::protobuf::io::CodedOutputStream* output) const;
-  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
+      bool deterministic, ::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {
+    return InternalSerializeWithCachedSizesToArray(false, output);
+  }
   int GetCachedSize() const { return _cached_size_; }
   private:
   void SharedCtor();
   void SharedDtor();
   void SetCachedSize(int size) const;
   void InternalSwap(ServerReflectionResponse* other);
+  void UnsafeMergeFrom(const ServerReflectionResponse& from);
   private:
   inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
     return _internal_metadata_.arena();
@@ -483,7 +507,6 @@
   inline void clear_has_message_response();
 
   ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
-  bool _is_default_instance_;
   ::google::protobuf::internal::ArenaStringPtr valid_host_;
   ::grpc::reflection::v1alpha::ServerReflectionRequest* original_request_;
   union MessageResponseUnion {
@@ -496,16 +519,18 @@
   mutable int _cached_size_;
   ::google::protobuf::uint32 _oneof_case_[1];
 
-  friend void  protobuf_AddDesc_reflection_2eproto();
+  friend void  protobuf_InitDefaults_reflection_2eproto_impl();
+  friend void  protobuf_AddDesc_reflection_2eproto_impl();
   friend void protobuf_AssignDesc_reflection_2eproto();
   friend void protobuf_ShutdownFile_reflection_2eproto();
 
   void InitAsDefaultInstance();
-  static ServerReflectionResponse* default_instance_;
 };
+extern ::google::protobuf::internal::ExplicitlyConstructed<ServerReflectionResponse> ServerReflectionResponse_default_instance_;
+
 // -------------------------------------------------------------------
 
-class FileDescriptorResponse : public ::google::protobuf::Message {
+class FileDescriptorResponse : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:grpc.reflection.v1alpha.FileDescriptorResponse) */ {
  public:
   FileDescriptorResponse();
   virtual ~FileDescriptorResponse();
@@ -520,6 +545,8 @@
   static const ::google::protobuf::Descriptor* descriptor();
   static const FileDescriptorResponse& default_instance();
 
+  static const FileDescriptorResponse* internal_default_instance();
+
   void Swap(FileDescriptorResponse* other);
 
   // implements Message ----------------------------------------------
@@ -534,18 +561,23 @@
   void Clear();
   bool IsInitialized() const;
 
-  int ByteSize() const;
+  size_t ByteSizeLong() const;
   bool MergePartialFromCodedStream(
       ::google::protobuf::io::CodedInputStream* input);
   void SerializeWithCachedSizes(
       ::google::protobuf::io::CodedOutputStream* output) const;
-  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
+      bool deterministic, ::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {
+    return InternalSerializeWithCachedSizesToArray(false, output);
+  }
   int GetCachedSize() const { return _cached_size_; }
   private:
   void SharedCtor();
   void SharedDtor();
   void SetCachedSize(int size) const;
   void InternalSwap(FileDescriptorResponse* other);
+  void UnsafeMergeFrom(const FileDescriptorResponse& from);
   private:
   inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
     return _internal_metadata_.arena();
@@ -581,19 +613,20 @@
  private:
 
   ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
-  bool _is_default_instance_;
   ::google::protobuf::RepeatedPtrField< ::std::string> file_descriptor_proto_;
   mutable int _cached_size_;
-  friend void  protobuf_AddDesc_reflection_2eproto();
+  friend void  protobuf_InitDefaults_reflection_2eproto_impl();
+  friend void  protobuf_AddDesc_reflection_2eproto_impl();
   friend void protobuf_AssignDesc_reflection_2eproto();
   friend void protobuf_ShutdownFile_reflection_2eproto();
 
   void InitAsDefaultInstance();
-  static FileDescriptorResponse* default_instance_;
 };
+extern ::google::protobuf::internal::ExplicitlyConstructed<FileDescriptorResponse> FileDescriptorResponse_default_instance_;
+
 // -------------------------------------------------------------------
 
-class ExtensionNumberResponse : public ::google::protobuf::Message {
+class ExtensionNumberResponse : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:grpc.reflection.v1alpha.ExtensionNumberResponse) */ {
  public:
   ExtensionNumberResponse();
   virtual ~ExtensionNumberResponse();
@@ -608,6 +641,8 @@
   static const ::google::protobuf::Descriptor* descriptor();
   static const ExtensionNumberResponse& default_instance();
 
+  static const ExtensionNumberResponse* internal_default_instance();
+
   void Swap(ExtensionNumberResponse* other);
 
   // implements Message ----------------------------------------------
@@ -622,18 +657,23 @@
   void Clear();
   bool IsInitialized() const;
 
-  int ByteSize() const;
+  size_t ByteSizeLong() const;
   bool MergePartialFromCodedStream(
       ::google::protobuf::io::CodedInputStream* input);
   void SerializeWithCachedSizes(
       ::google::protobuf::io::CodedOutputStream* output) const;
-  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
+      bool deterministic, ::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {
+    return InternalSerializeWithCachedSizesToArray(false, output);
+  }
   int GetCachedSize() const { return _cached_size_; }
   private:
   void SharedCtor();
   void SharedDtor();
   void SetCachedSize(int size) const;
   void InternalSwap(ExtensionNumberResponse* other);
+  void UnsafeMergeFrom(const ExtensionNumberResponse& from);
   private:
   inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
     return _internal_metadata_.arena();
@@ -676,21 +716,22 @@
  private:
 
   ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
-  bool _is_default_instance_;
-  ::google::protobuf::internal::ArenaStringPtr base_type_name_;
   ::google::protobuf::RepeatedField< ::google::protobuf::int32 > extension_number_;
   mutable int _extension_number_cached_byte_size_;
+  ::google::protobuf::internal::ArenaStringPtr base_type_name_;
   mutable int _cached_size_;
-  friend void  protobuf_AddDesc_reflection_2eproto();
+  friend void  protobuf_InitDefaults_reflection_2eproto_impl();
+  friend void  protobuf_AddDesc_reflection_2eproto_impl();
   friend void protobuf_AssignDesc_reflection_2eproto();
   friend void protobuf_ShutdownFile_reflection_2eproto();
 
   void InitAsDefaultInstance();
-  static ExtensionNumberResponse* default_instance_;
 };
+extern ::google::protobuf::internal::ExplicitlyConstructed<ExtensionNumberResponse> ExtensionNumberResponse_default_instance_;
+
 // -------------------------------------------------------------------
 
-class ListServiceResponse : public ::google::protobuf::Message {
+class ListServiceResponse : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:grpc.reflection.v1alpha.ListServiceResponse) */ {
  public:
   ListServiceResponse();
   virtual ~ListServiceResponse();
@@ -705,6 +746,8 @@
   static const ::google::protobuf::Descriptor* descriptor();
   static const ListServiceResponse& default_instance();
 
+  static const ListServiceResponse* internal_default_instance();
+
   void Swap(ListServiceResponse* other);
 
   // implements Message ----------------------------------------------
@@ -719,18 +762,23 @@
   void Clear();
   bool IsInitialized() const;
 
-  int ByteSize() const;
+  size_t ByteSizeLong() const;
   bool MergePartialFromCodedStream(
       ::google::protobuf::io::CodedInputStream* input);
   void SerializeWithCachedSizes(
       ::google::protobuf::io::CodedOutputStream* output) const;
-  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
+      bool deterministic, ::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {
+    return InternalSerializeWithCachedSizesToArray(false, output);
+  }
   int GetCachedSize() const { return _cached_size_; }
   private:
   void SharedCtor();
   void SharedDtor();
   void SetCachedSize(int size) const;
   void InternalSwap(ListServiceResponse* other);
+  void UnsafeMergeFrom(const ListServiceResponse& from);
   private:
   inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
     return _internal_metadata_.arena();
@@ -762,19 +810,20 @@
  private:
 
   ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
-  bool _is_default_instance_;
   ::google::protobuf::RepeatedPtrField< ::grpc::reflection::v1alpha::ServiceResponse > service_;
   mutable int _cached_size_;
-  friend void  protobuf_AddDesc_reflection_2eproto();
+  friend void  protobuf_InitDefaults_reflection_2eproto_impl();
+  friend void  protobuf_AddDesc_reflection_2eproto_impl();
   friend void protobuf_AssignDesc_reflection_2eproto();
   friend void protobuf_ShutdownFile_reflection_2eproto();
 
   void InitAsDefaultInstance();
-  static ListServiceResponse* default_instance_;
 };
+extern ::google::protobuf::internal::ExplicitlyConstructed<ListServiceResponse> ListServiceResponse_default_instance_;
+
 // -------------------------------------------------------------------
 
-class ServiceResponse : public ::google::protobuf::Message {
+class ServiceResponse : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:grpc.reflection.v1alpha.ServiceResponse) */ {
  public:
   ServiceResponse();
   virtual ~ServiceResponse();
@@ -789,6 +838,8 @@
   static const ::google::protobuf::Descriptor* descriptor();
   static const ServiceResponse& default_instance();
 
+  static const ServiceResponse* internal_default_instance();
+
   void Swap(ServiceResponse* other);
 
   // implements Message ----------------------------------------------
@@ -803,18 +854,23 @@
   void Clear();
   bool IsInitialized() const;
 
-  int ByteSize() const;
+  size_t ByteSizeLong() const;
   bool MergePartialFromCodedStream(
       ::google::protobuf::io::CodedInputStream* input);
   void SerializeWithCachedSizes(
       ::google::protobuf::io::CodedOutputStream* output) const;
-  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
+      bool deterministic, ::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {
+    return InternalSerializeWithCachedSizesToArray(false, output);
+  }
   int GetCachedSize() const { return _cached_size_; }
   private:
   void SharedCtor();
   void SharedDtor();
   void SetCachedSize(int size) const;
   void InternalSwap(ServiceResponse* other);
+  void UnsafeMergeFrom(const ServiceResponse& from);
   private:
   inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
     return _internal_metadata_.arena();
@@ -845,19 +901,20 @@
  private:
 
   ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
-  bool _is_default_instance_;
   ::google::protobuf::internal::ArenaStringPtr name_;
   mutable int _cached_size_;
-  friend void  protobuf_AddDesc_reflection_2eproto();
+  friend void  protobuf_InitDefaults_reflection_2eproto_impl();
+  friend void  protobuf_AddDesc_reflection_2eproto_impl();
   friend void protobuf_AssignDesc_reflection_2eproto();
   friend void protobuf_ShutdownFile_reflection_2eproto();
 
   void InitAsDefaultInstance();
-  static ServiceResponse* default_instance_;
 };
+extern ::google::protobuf::internal::ExplicitlyConstructed<ServiceResponse> ServiceResponse_default_instance_;
+
 // -------------------------------------------------------------------
 
-class ErrorResponse : public ::google::protobuf::Message {
+class ErrorResponse : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:grpc.reflection.v1alpha.ErrorResponse) */ {
  public:
   ErrorResponse();
   virtual ~ErrorResponse();
@@ -872,6 +929,8 @@
   static const ::google::protobuf::Descriptor* descriptor();
   static const ErrorResponse& default_instance();
 
+  static const ErrorResponse* internal_default_instance();
+
   void Swap(ErrorResponse* other);
 
   // implements Message ----------------------------------------------
@@ -886,18 +945,23 @@
   void Clear();
   bool IsInitialized() const;
 
-  int ByteSize() const;
+  size_t ByteSizeLong() const;
   bool MergePartialFromCodedStream(
       ::google::protobuf::io::CodedInputStream* input);
   void SerializeWithCachedSizes(
       ::google::protobuf::io::CodedOutputStream* output) const;
-  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
+      bool deterministic, ::google::protobuf::uint8* output) const;
+  ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {
+    return InternalSerializeWithCachedSizesToArray(false, output);
+  }
   int GetCachedSize() const { return _cached_size_; }
   private:
   void SharedCtor();
   void SharedDtor();
   void SetCachedSize(int size) const;
   void InternalSwap(ErrorResponse* other);
+  void UnsafeMergeFrom(const ErrorResponse& from);
   private:
   inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
     return _internal_metadata_.arena();
@@ -934,17 +998,18 @@
  private:
 
   ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
-  bool _is_default_instance_;
   ::google::protobuf::internal::ArenaStringPtr error_message_;
   ::google::protobuf::int32 error_code_;
   mutable int _cached_size_;
-  friend void  protobuf_AddDesc_reflection_2eproto();
+  friend void  protobuf_InitDefaults_reflection_2eproto_impl();
+  friend void  protobuf_AddDesc_reflection_2eproto_impl();
   friend void protobuf_AssignDesc_reflection_2eproto();
   friend void protobuf_ShutdownFile_reflection_2eproto();
 
   void InitAsDefaultInstance();
-  static ErrorResponse* default_instance_;
 };
+extern ::google::protobuf::internal::ExplicitlyConstructed<ErrorResponse> ErrorResponse_default_instance_;
+
 // ===================================================================
 
 
@@ -1378,6 +1443,9 @@
 inline ServerReflectionRequest::MessageRequestCase ServerReflectionRequest::message_request_case() const {
   return ServerReflectionRequest::MessageRequestCase(_oneof_case_[0]);
 }
+inline const ServerReflectionRequest* ServerReflectionRequest::internal_default_instance() {
+  return &ServerReflectionRequest_default_instance_.get();
+}
 // -------------------------------------------------------------------
 
 // ExtensionRequest
@@ -1440,6 +1508,9 @@
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ExtensionRequest.extension_number)
 }
 
+inline const ExtensionRequest* ExtensionRequest::internal_default_instance() {
+  return &ExtensionRequest_default_instance_.get();
+}
 // -------------------------------------------------------------------
 
 // ServerReflectionResponse
@@ -1490,7 +1561,7 @@
 
 // optional .grpc.reflection.v1alpha.ServerReflectionRequest original_request = 2;
 inline bool ServerReflectionResponse::has_original_request() const {
-  return !_is_default_instance_ && original_request_ != NULL;
+  return this != internal_default_instance() && original_request_ != NULL;
 }
 inline void ServerReflectionResponse::clear_original_request() {
   if (GetArenaNoVirtual() == NULL && original_request_ != NULL) delete original_request_;
@@ -1498,7 +1569,8 @@
 }
 inline const ::grpc::reflection::v1alpha::ServerReflectionRequest& ServerReflectionResponse::original_request() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ServerReflectionResponse.original_request)
-  return original_request_ != NULL ? *original_request_ : *default_instance_->original_request_;
+  return original_request_ != NULL ? *original_request_
+                         : *::grpc::reflection::v1alpha::ServerReflectionRequest::internal_default_instance();
 }
 inline ::grpc::reflection::v1alpha::ServerReflectionRequest* ServerReflectionResponse::mutable_original_request() {
   
@@ -1727,6 +1799,9 @@
 inline ServerReflectionResponse::MessageResponseCase ServerReflectionResponse::message_response_case() const {
   return ServerReflectionResponse::MessageResponseCase(_oneof_case_[0]);
 }
+inline const ServerReflectionResponse* ServerReflectionResponse::internal_default_instance() {
+  return &ServerReflectionResponse_default_instance_.get();
+}
 // -------------------------------------------------------------------
 
 // FileDescriptorResponse
@@ -1786,6 +1861,9 @@
   return &file_descriptor_proto_;
 }
 
+inline const FileDescriptorResponse* FileDescriptorResponse::internal_default_instance() {
+  return &FileDescriptorResponse_default_instance_.get();
+}
 // -------------------------------------------------------------------
 
 // ExtensionNumberResponse
@@ -1864,6 +1942,9 @@
   return &extension_number_;
 }
 
+inline const ExtensionNumberResponse* ExtensionNumberResponse::internal_default_instance() {
+  return &ExtensionNumberResponse_default_instance_.get();
+}
 // -------------------------------------------------------------------
 
 // ListServiceResponse
@@ -1898,6 +1979,9 @@
   return service_;
 }
 
+inline const ListServiceResponse* ListServiceResponse::internal_default_instance() {
+  return &ListServiceResponse_default_instance_.get();
+}
 // -------------------------------------------------------------------
 
 // ServiceResponse
@@ -1946,6 +2030,9 @@
   // @@protoc_insertion_point(field_set_allocated:grpc.reflection.v1alpha.ServiceResponse.name)
 }
 
+inline const ServiceResponse* ServiceResponse::internal_default_instance() {
+  return &ServiceResponse_default_instance_.get();
+}
 // -------------------------------------------------------------------
 
 // ErrorResponse
@@ -2008,6 +2095,9 @@
   // @@protoc_insertion_point(field_set_allocated:grpc.reflection.v1alpha.ErrorResponse.error_message)
 }
 
+inline const ErrorResponse* ErrorResponse::internal_default_instance() {
+  return &ErrorResponse_default_instance_.get();
+}
 #endif  // !PROTOBUF_INLINE_NOT_IN_HEADERS
 // -------------------------------------------------------------------
 
diff --git a/include/grpc++/grpc++.h b/include/grpc++/grpc++.h
index ef07e19..afb1c55 100644
--- a/include/grpc++/grpc++.h
+++ b/include/grpc++/grpc++.h
@@ -51,6 +51,9 @@
 #ifndef GRPCXX_GRPCXX_H
 #define GRPCXX_GRPCXX_H
 
+// Pragma for http://include-what-you-use.org/ tool, tells that following
+// headers are not private for grpc++.h and are part of its interface.
+// IWYU pragma: begin_exports
 #include <grpc/grpc.h>
 
 #include <grpc++/channel.h>
@@ -62,5 +65,6 @@
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
 #include <grpc++/server_posix.h>
+// IWYU pragma: end_exports
 
 #endif  // GRPCXX_GRPCXX_H
diff --git a/include/grpc++/impl/codegen/async_unary_call.h b/include/grpc++/impl/codegen/async_unary_call.h
index 5ceab73..87c94d6 100644
--- a/include/grpc++/impl/codegen/async_unary_call.h
+++ b/include/grpc++/impl/codegen/async_unary_call.h
@@ -40,7 +40,6 @@
 #include <grpc++/impl/codegen/server_context.h>
 #include <grpc++/impl/codegen/service_type.h>
 #include <grpc++/impl/codegen/status.h>
-#include <grpc/impl/codegen/log.h>
 
 namespace grpc {
 
diff --git a/include/grpc++/impl/codegen/call.h b/include/grpc++/impl/codegen/call.h
index dfac177..e211373 100644
--- a/include/grpc++/impl/codegen/call.h
+++ b/include/grpc++/impl/codegen/call.h
@@ -46,9 +46,9 @@
 #include <grpc++/impl/codegen/core_codegen_interface.h>
 #include <grpc++/impl/codegen/serialization_traits.h>
 #include <grpc++/impl/codegen/status.h>
+#include <grpc++/impl/codegen/status_helper.h>
 #include <grpc++/impl/codegen/string_ref.h>
 
-#include <grpc/impl/codegen/alloc.h>
 #include <grpc/impl/codegen/compression_types.h>
 #include <grpc/impl/codegen/grpc_types.h>
 
@@ -175,7 +175,7 @@
 class CallNoOp {
  protected:
   void AddOp(grpc_op* ops, size_t* nops) {}
-  void FinishOp(bool* status, int max_message_size) {}
+  void FinishOp(bool* status, int max_receive_message_size) {}
 };
 
 class CallOpSendInitialMetadata {
@@ -213,7 +213,7 @@
     op->data.send_initial_metadata.maybe_compression_level.level =
         maybe_compression_level_.level;
   }
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (!send_) return;
     g_core_codegen_interface->gpr_free(initial_metadata_);
     send_ = false;
@@ -253,7 +253,7 @@
     // Flags are per-message: clear them after use.
     write_options_.Clear();
   }
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (own_buf_) g_core_codegen_interface->grpc_byte_buffer_destroy(send_buf_);
     send_buf_ = nullptr;
   }
@@ -301,13 +301,14 @@
     op->data.recv_message = &recv_buf_;
   }
 
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (message_ == nullptr) return;
     if (recv_buf_) {
       if (*status) {
-        got_message = *status = SerializationTraits<R>::Deserialize(
-                                    recv_buf_, message_, max_message_size)
-                                    .ok();
+        got_message = *status =
+            SerializationTraits<R>::Deserialize(recv_buf_, message_,
+                                                max_receive_message_size)
+                .ok();
       } else {
         got_message = false;
         g_core_codegen_interface->grpc_byte_buffer_destroy(recv_buf_);
@@ -330,7 +331,8 @@
 namespace CallOpGenericRecvMessageHelper {
 class DeserializeFunc {
  public:
-  virtual Status Deserialize(grpc_byte_buffer* buf, int max_message_size) = 0;
+  virtual Status Deserialize(grpc_byte_buffer* buf,
+                             int max_receive_message_size) = 0;
   virtual ~DeserializeFunc() {}
 };
 
@@ -339,8 +341,9 @@
  public:
   DeserializeFuncType(R* message) : message_(message) {}
   Status Deserialize(grpc_byte_buffer* buf,
-                     int max_message_size) GRPC_OVERRIDE {
-    return SerializationTraits<R>::Deserialize(buf, message_, max_message_size);
+                     int max_receive_message_size) GRPC_OVERRIDE {
+    return SerializationTraits<R>::Deserialize(buf, message_,
+                                               max_receive_message_size);
   }
 
   ~DeserializeFuncType() GRPC_OVERRIDE {}
@@ -379,12 +382,13 @@
     op->data.recv_message = &recv_buf_;
   }
 
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (!deserialize_) return;
     if (recv_buf_) {
       if (*status) {
         got_message = true;
-        *status = deserialize_->Deserialize(recv_buf_, max_message_size).ok();
+        *status =
+            deserialize_->Deserialize(recv_buf_, max_receive_message_size).ok();
       } else {
         got_message = false;
         g_core_codegen_interface->grpc_byte_buffer_destroy(recv_buf_);
@@ -418,7 +422,7 @@
     op->flags = 0;
     op->reserved = NULL;
   }
-  void FinishOp(bool* status, int max_message_size) { send_ = false; }
+  void FinishOp(bool* status, int max_receive_message_size) { send_ = false; }
 
  private:
   bool send_;
@@ -434,7 +438,7 @@
     trailing_metadata_count_ = trailing_metadata.size();
     trailing_metadata_ = FillMetadataArray(trailing_metadata);
     send_status_available_ = true;
-    send_status_code_ = static_cast<grpc_status_code>(status.error_code());
+    send_status_code_ = static_cast<grpc_status_code>(GetCanonicalCode(status));
     send_status_details_ = status.error_message();
   }
 
@@ -453,7 +457,7 @@
     op->reserved = NULL;
   }
 
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (!send_status_available_) return;
     g_core_codegen_interface->gpr_free(trailing_metadata_);
     send_status_available_ = false;
@@ -486,7 +490,7 @@
     op->flags = 0;
     op->reserved = NULL;
   }
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (recv_initial_metadata_ == nullptr) return;
     FillMetadataMap(&recv_initial_metadata_arr_, recv_initial_metadata_);
     recv_initial_metadata_ = nullptr;
@@ -525,7 +529,7 @@
     op->reserved = NULL;
   }
 
-  void FinishOp(bool* status, int max_message_size) {
+  void FinishOp(bool* status, int max_receive_message_size) {
     if (recv_status_ == nullptr) return;
     FillMetadataMap(&recv_trailing_metadata_arr_, recv_trailing_metadata_);
     *recv_status_ = Status(
@@ -562,13 +566,13 @@
 /// API.
 class CallOpSetInterface : public CompletionQueueTag {
  public:
-  CallOpSetInterface() : max_message_size_(0) {}
+  CallOpSetInterface() : max_receive_message_size_(0) {}
   /// Fills in grpc_op, starting from ops[*nops] and moving
   /// upwards.
   virtual void FillOps(grpc_op* ops, size_t* nops) = 0;
 
-  void set_max_message_size(int max_message_size) {
-    max_message_size_ = max_message_size;
+  void set_max_receive_message_size(int max_receive_message_size) {
+    max_receive_message_size_ = max_receive_message_size;
   }
 
   /// Mark this as belonging to a collection if needed
@@ -577,7 +581,7 @@
   }
 
  protected:
-  int max_message_size_;
+  int max_receive_message_size_;
   std::shared_ptr<CallOpSetCollectionInterface> collection_;
 };
 
@@ -609,12 +613,12 @@
   }
 
   bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE {
-    this->Op1::FinishOp(status, max_message_size_);
-    this->Op2::FinishOp(status, max_message_size_);
-    this->Op3::FinishOp(status, max_message_size_);
-    this->Op4::FinishOp(status, max_message_size_);
-    this->Op5::FinishOp(status, max_message_size_);
-    this->Op6::FinishOp(status, max_message_size_);
+    this->Op1::FinishOp(status, max_receive_message_size_);
+    this->Op2::FinishOp(status, max_receive_message_size_);
+    this->Op3::FinishOp(status, max_receive_message_size_);
+    this->Op4::FinishOp(status, max_receive_message_size_);
+    this->Op5::FinishOp(status, max_receive_message_size_);
+    this->Op6::FinishOp(status, max_receive_message_size_);
     *tag = return_tag_;
     collection_.reset();  // drop the ref at this point
     return true;
@@ -646,32 +650,35 @@
  public:
   /* call is owned by the caller */
   Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq)
-      : call_hook_(call_hook), cq_(cq), call_(call), max_message_size_(-1) {}
-
-  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq,
-       int max_message_size)
       : call_hook_(call_hook),
         cq_(cq),
         call_(call),
-        max_message_size_(max_message_size) {}
+        max_receive_message_size_(-1) {}
+
+  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq,
+       int max_receive_message_size)
+      : call_hook_(call_hook),
+        cq_(cq),
+        call_(call),
+        max_receive_message_size_(max_receive_message_size) {}
 
   void PerformOps(CallOpSetInterface* ops) {
-    if (max_message_size_ > 0) {
-      ops->set_max_message_size(max_message_size_);
+    if (max_receive_message_size_ > 0) {
+      ops->set_max_receive_message_size(max_receive_message_size_);
     }
     call_hook_->PerformOpsOnCall(ops, this);
   }
 
-  grpc_call* call() { return call_; }
-  CompletionQueue* cq() { return cq_; }
+  grpc_call* call() const { return call_; }
+  CompletionQueue* cq() const { return cq_; }
 
-  int max_message_size() { return max_message_size_; }
+  int max_receive_message_size() { return max_receive_message_size_; }
 
  private:
   CallHook* call_hook_;
   CompletionQueue* cq_;
   grpc_call* call_;
-  int max_message_size_;
+  int max_receive_message_size_;
 };
 
 }  // namespace grpc
diff --git a/include/grpc++/impl/codegen/client_context.h b/include/grpc++/impl/codegen/client_context.h
index 012bcc2..387d807 100644
--- a/include/grpc++/impl/codegen/client_context.h
+++ b/include/grpc++/impl/codegen/client_context.h
@@ -62,9 +62,7 @@
 #include <grpc++/impl/codegen/sync.h>
 #include <grpc++/impl/codegen/time.h>
 #include <grpc/impl/codegen/compression_types.h>
-#include <grpc/impl/codegen/log.h>
 #include <grpc/impl/codegen/propagation_bits.h>
-#include <grpc/impl/codegen/time.h>
 
 struct census_context;
 struct grpc_call;
@@ -225,6 +223,9 @@
   /// EXPERIMENTAL: Set this request to be idempotent
   void set_idempotent(bool idempotent) { idempotent_ = idempotent; }
 
+  /// EXPERIMENTAL: Set this request to be cacheable
+  void set_cacheable(bool cacheable) { cacheable_ = cacheable; }
+
   /// EXPERIMENTAL: Trigger fail-fast or not on this request
   void set_fail_fast(bool fail_fast) { fail_fast_ = fail_fast; }
 
@@ -271,7 +272,7 @@
 
   /// Set \a algorithm to be the compression algorithm used for the client call.
   ///
-  /// \param algorith The compression algorithm used for the client call.
+  /// \param algorithm The compression algorithm used for the client call.
   void set_compression_algorithm(grpc_compression_algorithm algorithm);
 
   /// Return the peer uri in a string.
@@ -307,6 +308,10 @@
   };
   static void SetGlobalCallbacks(GlobalCallbacks* callbacks);
 
+  // Should be used for framework-level extensions only.
+  // Applications never need to call this method.
+  grpc_call* c_call() { return call_; }
+
  private:
   // Disallow copy and assign.
   ClientContext(const ClientContext&);
@@ -342,7 +347,8 @@
 
   uint32_t initial_metadata_flags() const {
     return (idempotent_ ? GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST : 0) |
-           (fail_fast_ ? 0 : GRPC_INITIAL_METADATA_IGNORE_CONNECTIVITY);
+           (fail_fast_ ? 0 : GRPC_INITIAL_METADATA_IGNORE_CONNECTIVITY) |
+           (cacheable_ ? GRPC_INITIAL_METADATA_CACHEABLE_REQUEST : 0);
   }
 
   grpc::string authority() { return authority_; }
@@ -350,6 +356,7 @@
   bool initial_metadata_received_;
   bool fail_fast_;
   bool idempotent_;
+  bool cacheable_;
   std::shared_ptr<Channel> channel_;
   grpc::mutex mu_;
   grpc_call* call_;
diff --git a/include/grpc++/impl/codegen/completion_queue.h b/include/grpc++/impl/codegen/completion_queue.h
index 03009e0..29bc97f 100644
--- a/include/grpc++/impl/codegen/completion_queue.h
+++ b/include/grpc++/impl/codegen/completion_queue.h
@@ -52,7 +52,6 @@
 #include <grpc++/impl/codegen/grpc_library.h>
 #include <grpc++/impl/codegen/status.h>
 #include <grpc++/impl/codegen/time.h>
-#include <grpc/impl/codegen/time.h>
 
 struct grpc_completion_queue;
 
@@ -68,8 +67,10 @@
 class ServerReader;
 template <class W>
 class ServerWriter;
+namespace internal {
 template <class W, class R>
-class ServerReaderWriter;
+class ServerReaderWriterBody;
+}
 template <class ServiceType, class RequestType, class ResponseType>
 class RpcMethodHandler;
 template <class ServiceType, class RequestType, class ResponseType>
@@ -178,15 +179,15 @@
   template <class W>
   friend class ::grpc::ServerWriter;
   template <class W, class R>
-  friend class ::grpc::ServerReaderWriter;
+  friend class ::grpc::internal::ServerReaderWriterBody;
   template <class ServiceType, class RequestType, class ResponseType>
   friend class RpcMethodHandler;
   template <class ServiceType, class RequestType, class ResponseType>
   friend class ClientStreamingHandler;
   template <class ServiceType, class RequestType, class ResponseType>
   friend class ServerStreamingHandler;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class BidiStreamingHandler;
+  template <class Streamer, bool WriteNeeded>
+  friend class TemplatedBidiStreamingHandler;
   friend class UnknownMethodHandler;
   friend class ::grpc::Server;
   friend class ::grpc::ServerContext;
@@ -217,7 +218,7 @@
   /// Performs a single polling pluck on \a tag.
   /// \warning Must not be mixed with calls to \a Next.
   void TryPluck(CompletionQueueTag* tag) {
-    auto deadline = gpr_time_0(GPR_CLOCK_REALTIME);
+    auto deadline = g_core_codegen_interface->gpr_time_0(GPR_CLOCK_REALTIME);
     auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
         cq_, tag, deadline, nullptr);
     if (ev.type == GRPC_QUEUE_TIMEOUT) return;
diff --git a/include/grpc++/impl/codegen/core_codegen.h b/include/grpc++/impl/codegen/core_codegen.h
index 9699abf..0ce009e 100644
--- a/include/grpc++/impl/codegen/core_codegen.h
+++ b/include/grpc++/impl/codegen/core_codegen.h
@@ -31,6 +31,9 @@
  *
  */
 
+#ifndef GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_H
+#define GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_H
+
 // This file should be compiled as part of grpc++.
 
 #include <grpc++/impl/codegen/core_codegen_interface.h>
@@ -52,6 +55,17 @@
   void* gpr_malloc(size_t size) GRPC_OVERRIDE;
   void gpr_free(void* p) GRPC_OVERRIDE;
 
+  void gpr_mu_init(gpr_mu* mu) GRPC_OVERRIDE;
+  void gpr_mu_destroy(gpr_mu* mu) GRPC_OVERRIDE;
+  void gpr_mu_lock(gpr_mu* mu) GRPC_OVERRIDE;
+  void gpr_mu_unlock(gpr_mu* mu) GRPC_OVERRIDE;
+  void gpr_cv_init(gpr_cv* cv) GRPC_OVERRIDE;
+  void gpr_cv_destroy(gpr_cv* cv) GRPC_OVERRIDE;
+  int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu,
+                  gpr_timespec abs_deadline) GRPC_OVERRIDE;
+  void gpr_cv_signal(gpr_cv* cv) GRPC_OVERRIDE;
+  void gpr_cv_broadcast(gpr_cv* cv) GRPC_OVERRIDE;
+
   void grpc_byte_buffer_destroy(grpc_byte_buffer* bb) GRPC_OVERRIDE;
 
   int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader,
@@ -75,6 +89,7 @@
   void grpc_metadata_array_destroy(grpc_metadata_array* array) GRPC_OVERRIDE;
 
   gpr_timespec gpr_inf_future(gpr_clock_type type) GRPC_OVERRIDE;
+  gpr_timespec gpr_time_0(gpr_clock_type type) GRPC_OVERRIDE;
 
   virtual const Status& ok() GRPC_OVERRIDE;
   virtual const Status& cancelled() GRPC_OVERRIDE;
@@ -83,3 +98,5 @@
 };
 
 }  // namespace grpc
+
+#endif  // GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_H
diff --git a/include/grpc++/impl/codegen/core_codegen_interface.h b/include/grpc++/impl/codegen/core_codegen_interface.h
index f9a8f9b..68851f1 100644
--- a/include/grpc++/impl/codegen/core_codegen_interface.h
+++ b/include/grpc++/impl/codegen/core_codegen_interface.h
@@ -36,7 +36,13 @@
 
 #include <grpc++/impl/codegen/config.h>
 #include <grpc++/impl/codegen/status.h>
+#include <grpc/impl/codegen/byte_buffer_reader.h>
 #include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/impl/codegen/sync.h>
+
+extern "C" {
+struct grpc_byte_buffer;
+}
 
 namespace grpc {
 
@@ -63,6 +69,17 @@
   virtual void* gpr_malloc(size_t size) = 0;
   virtual void gpr_free(void* p) = 0;
 
+  virtual void gpr_mu_init(gpr_mu* mu) = 0;
+  virtual void gpr_mu_destroy(gpr_mu* mu) = 0;
+  virtual void gpr_mu_lock(gpr_mu* mu) = 0;
+  virtual void gpr_mu_unlock(gpr_mu* mu) = 0;
+  virtual void gpr_cv_init(gpr_cv* cv) = 0;
+  virtual void gpr_cv_destroy(gpr_cv* cv) = 0;
+  virtual int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu,
+                          gpr_timespec abs_deadline) = 0;
+  virtual void gpr_cv_signal(gpr_cv* cv) = 0;
+  virtual void gpr_cv_broadcast(gpr_cv* cv) = 0;
+
   virtual void grpc_byte_buffer_destroy(grpc_byte_buffer* bb) = 0;
 
   virtual int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader,
@@ -89,6 +106,7 @@
   virtual const Status& cancelled() = 0;
 
   virtual gpr_timespec gpr_inf_future(gpr_clock_type type) = 0;
+  virtual gpr_timespec gpr_time_0(gpr_clock_type type) = 0;
 };
 
 extern CoreCodegenInterface* g_core_codegen_interface;
diff --git a/include/grpc++/impl/codegen/grpc_library.h b/include/grpc++/impl/codegen/grpc_library.h
index 3cdc6f3..2b11aff 100644
--- a/include/grpc++/impl/codegen/grpc_library.h
+++ b/include/grpc++/impl/codegen/grpc_library.h
@@ -35,7 +35,6 @@
 #define GRPCXX_IMPL_CODEGEN_GRPC_LIBRARY_H
 
 #include <grpc++/impl/codegen/core_codegen_interface.h>
-#include <grpc/impl/codegen/log.h>
 
 namespace grpc {
 
diff --git a/include/grpc++/impl/codegen/method_handler_impl.h b/include/grpc++/impl/codegen/method_handler_impl.h
index 2f4be64..52f9276 100644
--- a/include/grpc++/impl/codegen/method_handler_impl.h
+++ b/include/grpc++/impl/codegen/method_handler_impl.h
@@ -53,7 +53,7 @@
   void RunHandler(const HandlerParameter& param) GRPC_FINAL {
     RequestType req;
     Status status = SerializationTraits<RequestType>::Deserialize(
-        param.request, &req, param.max_message_size);
+        param.request, &req, param.max_receive_message_size);
     ResponseType rsp;
     if (status.ok()) {
       status = func_(service_, param.server_context, &req, &rsp);
@@ -139,7 +139,7 @@
   void RunHandler(const HandlerParameter& param) GRPC_FINAL {
     RequestType req;
     Status status = SerializationTraits<RequestType>::Deserialize(
-        param.request, &req, param.max_message_size);
+        param.request, &req, param.max_receive_message_size);
 
     if (status.ok()) {
       ServerWriter<ResponseType> writer(param.call, param.server_context);
@@ -167,20 +167,22 @@
 };
 
 // A wrapper class of an application provided bidi-streaming handler.
-template <class ServiceType, class RequestType, class ResponseType>
-class BidiStreamingHandler : public MethodHandler {
+// This also applies to server-streamed implementation of a unary method
+// with the additional requirement that such methods must have done a
+// write for status to be ok
+// Since this is used by more than 1 class, the service is not passed in.
+// Instead, it is expected to be an implicitly-captured argument of func
+// (through bind or something along those lines)
+template <class Streamer, bool WriteNeeded>
+class TemplatedBidiStreamingHandler : public MethodHandler {
  public:
-  BidiStreamingHandler(
-      std::function<Status(ServiceType*, ServerContext*,
-                           ServerReaderWriter<ResponseType, RequestType>*)>
-          func,
-      ServiceType* service)
-      : func_(func), service_(service) {}
+  TemplatedBidiStreamingHandler(
+      std::function<Status(ServerContext*, Streamer*)> func)
+      : func_(func), write_needed_(WriteNeeded) {}
 
   void RunHandler(const HandlerParameter& param) GRPC_FINAL {
-    ServerReaderWriter<ResponseType, RequestType> stream(param.call,
-                                                         param.server_context);
-    Status status = func_(service_, param.server_context, &stream);
+    Streamer stream(param.call, param.server_context);
+    Status status = func_(param.server_context, &stream);
 
     CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops;
     if (!param.server_context->sent_initial_metadata_) {
@@ -189,6 +191,12 @@
       if (param.server_context->compression_level_set()) {
         ops.set_compression_level(param.server_context->compression_level());
       }
+      if (write_needed_ && status.ok()) {
+        // If we needed a write but never did one, we need to mark the
+        // status as a fail
+        status = Status(StatusCode::INTERNAL,
+                        "Service did not provide response message");
+      }
     }
     ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
     param.call->PerformOps(&ops);
@@ -196,10 +204,36 @@
   }
 
  private:
-  std::function<Status(ServiceType*, ServerContext*,
-                       ServerReaderWriter<ResponseType, RequestType>*)>
-      func_;
-  ServiceType* service_;
+  std::function<Status(ServerContext*, Streamer*)> func_;
+  const bool write_needed_;
+};
+
+template <class ServiceType, class RequestType, class ResponseType>
+class BidiStreamingHandler
+    : public TemplatedBidiStreamingHandler<
+          ServerReaderWriter<ResponseType, RequestType>, false> {
+ public:
+  BidiStreamingHandler(
+      std::function<Status(ServiceType*, ServerContext*,
+                           ServerReaderWriter<ResponseType, RequestType>*)>
+          func,
+      ServiceType* service)
+      : TemplatedBidiStreamingHandler<
+            ServerReaderWriter<ResponseType, RequestType>, false>(std::bind(
+            func, service, std::placeholders::_1, std::placeholders::_2)) {}
+};
+
+template <class RequestType, class ResponseType>
+class StreamedUnaryHandler
+    : public TemplatedBidiStreamingHandler<
+          ServerUnaryStreamer<RequestType, ResponseType>, true> {
+ public:
+  explicit StreamedUnaryHandler(
+      std::function<Status(ServerContext*,
+                           ServerUnaryStreamer<RequestType, ResponseType>*)>
+          func)
+      : TemplatedBidiStreamingHandler<
+            ServerUnaryStreamer<RequestType, ResponseType>, true>(func) {}
 };
 
 // Handle unknown method by returning UNIMPLEMENTED error.
diff --git a/include/grpc++/impl/codegen/proto_utils.h b/include/grpc++/impl/codegen/proto_utils.h
index d4599c5..6f4786b 100644
--- a/include/grpc++/impl/codegen/proto_utils.h
+++ b/include/grpc++/impl/codegen/proto_utils.h
@@ -40,9 +40,8 @@
 #include <grpc++/impl/codegen/core_codegen_interface.h>
 #include <grpc++/impl/codegen/serialization_traits.h>
 #include <grpc++/impl/codegen/status.h>
-#include <grpc/impl/codegen/byte_buffer.h>
 #include <grpc/impl/codegen/byte_buffer_reader.h>
-#include <grpc/impl/codegen/log.h>
+#include <grpc/impl/codegen/grpc_types.h>
 #include <grpc/impl/codegen/slice.h>
 
 namespace grpc {
@@ -205,7 +204,7 @@
 
   static Status Deserialize(grpc_byte_buffer* buffer,
                             grpc::protobuf::Message* msg,
-                            int max_message_size) {
+                            int max_receive_message_size) {
     if (buffer == nullptr) {
       return Status(StatusCode::INTERNAL, "No payload");
     }
@@ -216,8 +215,9 @@
         return reader.status();
       }
       ::grpc::protobuf::io::CodedInputStream decoder(&reader);
-      if (max_message_size > 0) {
-        decoder.SetTotalBytesLimit(max_message_size, max_message_size);
+      if (max_receive_message_size > 0) {
+        decoder.SetTotalBytesLimit(max_receive_message_size,
+                                   max_receive_message_size);
       }
       if (!msg->ParseFromCodedStream(&decoder)) {
         result = Status(StatusCode::INTERNAL, msg->InitializationErrorString());
diff --git a/include/grpc++/impl/codegen/rpc_method.h b/include/grpc++/impl/codegen/rpc_method.h
index 39cb4f7..4897428 100644
--- a/include/grpc++/impl/codegen/rpc_method.h
+++ b/include/grpc++/impl/codegen/rpc_method.h
@@ -60,11 +60,12 @@
 
   const char* name() const { return name_; }
   RpcType method_type() const { return method_type_; }
+  void SetMethodType(RpcType type) { method_type_ = type; }
   void* channel_tag() const { return channel_tag_; }
 
  private:
   const char* const name_;
-  const RpcType method_type_;
+  RpcType method_type_;
   void* const channel_tag_;
 };
 
diff --git a/include/grpc++/impl/codegen/rpc_service_method.h b/include/grpc++/impl/codegen/rpc_service_method.h
index 8b1f026..78c54e3 100644
--- a/include/grpc++/impl/codegen/rpc_service_method.h
+++ b/include/grpc++/impl/codegen/rpc_service_method.h
@@ -43,7 +43,10 @@
 #include <grpc++/impl/codegen/config.h>
 #include <grpc++/impl/codegen/rpc_method.h>
 #include <grpc++/impl/codegen/status.h>
-#include <grpc/impl/codegen/byte_buffer.h>
+
+extern "C" {
+struct grpc_byte_buffer;
+}
 
 namespace grpc {
 class ServerContext;
@@ -59,12 +62,12 @@
         : call(c),
           server_context(context),
           request(req),
-          max_message_size(max_size) {}
+          max_receive_message_size(max_size) {}
     Call* call;
     ServerContext* server_context;
     // Handler required to grpc_byte_buffer_destroy this
     grpc_byte_buffer* request;
-    int max_message_size;
+    int max_receive_message_size;
   };
   virtual void RunHandler(const HandlerParameter& param) = 0;
 };
@@ -82,6 +85,7 @@
   // if MethodHandler is nullptr, then this is an async method
   MethodHandler* handler() const { return handler_.get(); }
   void ResetHandler() { handler_.reset(); }
+  void SetHandler(MethodHandler* handler) { handler_.reset(handler); }
 
  private:
   void* server_tag_;
diff --git a/include/grpc++/impl/codegen/serialization_traits.h b/include/grpc++/impl/codegen/serialization_traits.h
index fa99dbf..54e5d47 100644
--- a/include/grpc++/impl/codegen/serialization_traits.h
+++ b/include/grpc++/impl/codegen/serialization_traits.h
@@ -43,10 +43,10 @@
 /// functions:
 ///   static Status Serialize(const Message& msg,
 ///                           grpc_byte_buffer** buffer,
-//                            bool* own_buffer);
+///                           bool* own_buffer);
 ///   static Status Deserialize(grpc_byte_buffer* buffer,
 ///                             Message* msg,
-///                             int max_message_size);
+///                             int max_receive_message_size);
 ///
 /// Serialize is required to convert message to a grpc_byte_buffer, and
 /// to store a pointer to that byte buffer at *buffer. *own_buffer should
@@ -54,8 +54,8 @@
 /// ownership is retained elsewhere.
 ///
 /// Deserialize is required to convert buffer into the message stored at
-/// msg. max_message_size is passed in as a bound on the maximum number of
-/// message bytes Deserialize should accept.
+/// msg. max_receive_message_size is passed in as a bound on the maximum
+/// number of message bytes Deserialize should accept.
 ///
 /// Both functions return a Status, allowing them to explain what went
 /// wrong if required.
diff --git a/include/grpc++/impl/codegen/server_context.h b/include/grpc++/impl/codegen/server_context.h
index 08212af..bce8d2c 100644
--- a/include/grpc++/impl/codegen/server_context.h
+++ b/include/grpc++/impl/codegen/server_context.h
@@ -43,7 +43,6 @@
 #include <grpc++/impl/codegen/string_ref.h>
 #include <grpc++/impl/codegen/time.h>
 #include <grpc/impl/codegen/compression_types.h>
-#include <grpc/impl/codegen/time.h>
 
 struct gpr_timespec;
 struct grpc_metadata;
@@ -65,8 +64,10 @@
 class ServerReader;
 template <class W>
 class ServerWriter;
+namespace internal {
 template <class W, class R>
-class ServerReaderWriter;
+class ServerReaderWriterBody;
+}
 template <class ServiceType, class RequestType, class ResponseType>
 class RpcMethodHandler;
 template <class ServiceType, class RequestType, class ResponseType>
@@ -166,6 +167,10 @@
     async_notify_when_done_tag_ = tag;
   }
 
+  // Should be used for framework-level extensions only.
+  // Applications never need to call this method.
+  grpc_call* c_call() { return call_; }
+
  private:
   friend class ::grpc::testing::InteropServerContextInspector;
   friend class ::grpc::ServerInterface;
@@ -183,15 +188,15 @@
   template <class W>
   friend class ::grpc::ServerWriter;
   template <class W, class R>
-  friend class ::grpc::ServerReaderWriter;
+  friend class ::grpc::internal::ServerReaderWriterBody;
   template <class ServiceType, class RequestType, class ResponseType>
   friend class RpcMethodHandler;
   template <class ServiceType, class RequestType, class ResponseType>
   friend class ClientStreamingHandler;
   template <class ServiceType, class RequestType, class ResponseType>
   friend class ServerStreamingHandler;
-  template <class ServiceType, class RequestType, class ResponseType>
-  friend class BidiStreamingHandler;
+  template <class Streamer, bool WriteNeeded>
+  friend class TemplatedBidiStreamingHandler;
   friend class UnknownMethodHandler;
   friend class ::grpc::ClientContext;
 
diff --git a/include/grpc++/impl/codegen/server_interface.h b/include/grpc++/impl/codegen/server_interface.h
index 3a3e052..4a00d7a 100644
--- a/include/grpc++/impl/codegen/server_interface.h
+++ b/include/grpc++/impl/codegen/server_interface.h
@@ -52,6 +52,8 @@
 class Service;
 class ThreadPoolInterface;
 
+extern CoreCodegenInterface* g_core_codegen_interface;
+
 /// Models a gRPC server.
 ///
 /// Servers are configured and started via \a grpc::ServerBuilder.
@@ -78,7 +80,10 @@
   /// All completion queue associated with the server (for example, for async
   /// serving) must be shutdown *after* this method has returned:
   /// See \a ServerBuilder::AddCompletionQueue for details.
-  void Shutdown() { ShutdownInternal(gpr_inf_future(GPR_CLOCK_MONOTONIC)); }
+  void Shutdown() {
+    ShutdownInternal(
+        g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_MONOTONIC));
+  }
 
   /// Block waiting for all work to complete.
   ///
@@ -129,7 +134,7 @@
 
   virtual void ShutdownInternal(gpr_timespec deadline) = 0;
 
-  virtual int max_message_size() const = 0;
+  virtual int max_receive_message_size() const = 0;
 
   virtual grpc_server* server() = 0;
 
@@ -200,8 +205,8 @@
     bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE {
       bool serialization_status =
           *status && payload_ &&
-          SerializationTraits<Message>::Deserialize(payload_, request_,
-                                                    server_->max_message_size())
+          SerializationTraits<Message>::Deserialize(
+              payload_, request_, server_->max_receive_message_size())
               .ok();
       bool ret = RegisteredAsyncRequest::FinalizeResult(tag, status);
       *status = serialization_status && *status;
diff --git a/include/grpc++/impl/codegen/service_type.h b/include/grpc++/impl/codegen/service_type.h
index c19dfc7..72b2225 100644
--- a/include/grpc++/impl/codegen/service_type.h
+++ b/include/grpc++/impl/codegen/service_type.h
@@ -147,6 +147,17 @@
     methods_[index].reset();
   }
 
+  void MarkMethodStreamedUnary(int index,
+                               MethodHandler* streamed_unary_method) {
+    GPR_CODEGEN_ASSERT(methods_[index] && methods_[index]->handler() &&
+                       "Cannot mark an async or generic method Streamed Unary");
+    methods_[index]->SetHandler(streamed_unary_method);
+
+    // From the server's point of view, streamed unary is a special
+    // case of BIDI_STREAMING that has 1 read and 1 write, in that order.
+    methods_[index]->SetMethodType(::grpc::RpcMethod::BIDI_STREAMING);
+  }
+
  private:
   friend class Server;
   friend class ServerInterface;
diff --git a/include/grpc++/impl/codegen/status_helper.h b/include/grpc++/impl/codegen/status_helper.h
new file mode 100644
index 0000000..bfe45d9
--- /dev/null
+++ b/include/grpc++/impl/codegen/status_helper.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2016, 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 GRPCXX_IMPL_CODEGEN_STATUS_HELPER_H
+#define GRPCXX_IMPL_CODEGEN_STATUS_HELPER_H
+
+#include <grpc++/impl/codegen/status.h>
+
+namespace grpc {
+
+inline StatusCode GetCanonicalCode(const Status& status) {
+  return status.error_code();
+}
+
+}  // namespace grpc
+
+#endif  // GRPCXX_IMPL_CODEGEN_STATUS_HELPER_H
diff --git a/include/grpc++/impl/codegen/sync_no_cxx11.h b/include/grpc++/impl/codegen/sync_no_cxx11.h
index 55c059e..87a6594 100644
--- a/include/grpc++/impl/codegen/sync_no_cxx11.h
+++ b/include/grpc++/impl/codegen/sync_no_cxx11.h
@@ -34,18 +34,20 @@
 #ifndef GRPCXX_IMPL_CODEGEN_SYNC_NO_CXX11_H
 #define GRPCXX_IMPL_CODEGEN_SYNC_NO_CXX11_H
 
-#include <grpc/impl/codegen/sync.h>
+#include <grpc++/impl/codegen/core_codegen_interface.h>
 
 namespace grpc {
 
+extern CoreCodegenInterface *g_core_codegen_interface;
+
 template <class mutex>
 class lock_guard;
 class condition_variable;
 
 class mutex {
  public:
-  mutex() { gpr_mu_init(&mu_); }
-  ~mutex() { gpr_mu_destroy(&mu_); }
+  mutex() { g_core_codegen_interface->gpr_mu_init(&mu_); }
+  ~mutex() { g_core_codegen_interface->gpr_mu_destroy(&mu_); }
 
  private:
   ::gpr_mu mu_;
@@ -57,16 +59,18 @@
 template <class mutex>
 class lock_guard {
  public:
-  lock_guard(mutex &mu) : mu_(mu), locked(true) { gpr_mu_lock(&mu.mu_); }
+  lock_guard(mutex &mu) : mu_(mu), locked(true) {
+    g_core_codegen_interface->gpr_mu_lock(&mu.mu_);
+  }
   ~lock_guard() { unlock_internal(); }
 
  protected:
   void lock_internal() {
-    if (!locked) gpr_mu_lock(&mu_.mu_);
+    if (!locked) g_core_codegen_interface->gpr_mu_lock(&mu_.mu_);
     locked = true;
   }
   void unlock_internal() {
-    if (locked) gpr_mu_unlock(&mu_.mu_);
+    if (locked) g_core_codegen_interface->gpr_mu_unlock(&mu_.mu_);
     locked = false;
   }
 
@@ -86,15 +90,17 @@
 
 class condition_variable {
  public:
-  condition_variable() { gpr_cv_init(&cv_); }
-  ~condition_variable() { gpr_cv_destroy(&cv_); }
+  condition_variable() { g_core_codegen_interface->gpr_cv_init(&cv_); }
+  ~condition_variable() { g_core_codegen_interface->gpr_cv_destroy(&cv_); }
   void wait(lock_guard<mutex> &mu) {
     mu.locked = false;
-    gpr_cv_wait(&cv_, &mu.mu_.mu_, gpr_inf_future(GPR_CLOCK_REALTIME));
+    g_core_codegen_interface->gpr_cv_wait(
+        &cv_, &mu.mu_.mu_,
+        g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_REALTIME));
     mu.locked = true;
   }
-  void notify_one() { gpr_cv_signal(&cv_); }
-  void notify_all() { gpr_cv_broadcast(&cv_); }
+  void notify_one() { g_core_codegen_interface->gpr_cv_signal(&cv_); }
+  void notify_all() { g_core_codegen_interface->gpr_cv_broadcast(&cv_); }
 
  private:
   gpr_cv cv_;
diff --git a/include/grpc++/impl/codegen/sync_stream.h b/include/grpc++/impl/codegen/sync_stream.h
index b2b9727..e3c5a91 100644
--- a/include/grpc++/impl/codegen/sync_stream.h
+++ b/include/grpc++/impl/codegen/sync_stream.h
@@ -42,7 +42,6 @@
 #include <grpc++/impl/codegen/server_context.h>
 #include <grpc++/impl/codegen/service_type.h>
 #include <grpc++/impl/codegen/status.h>
-#include <grpc/impl/codegen/log.h>
 
 namespace grpc {
 
@@ -64,12 +63,24 @@
   virtual Status Finish() = 0;
 };
 
+/// Common interface for all synchronous server side streaming.
+class ServerStreamingInterface {
+ public:
+  virtual ~ServerStreamingInterface() {}
+
+  /// Blocking send initial metadata to client.
+  virtual void SendInitialMetadata() = 0;
+};
+
 /// An interface that yields a sequence of messages of type \a R.
 template <class R>
 class ReaderInterface {
  public:
   virtual ~ReaderInterface() {}
 
+  /// Upper bound on the next message size available for reading on this stream
+  virtual bool NextMessageSize(uint32_t* sz) = 0;
+
   /// Blocking read a message and parse to \a msg. Returns \a true on success.
   /// This is thread-safe with respect to \a Write or \WritesDone methods on
   /// the same stream. It should not be called concurrently with another \a
@@ -148,6 +159,11 @@
     cq_.Pluck(&ops);  /// status ignored
   }
 
+  bool NextMessageSize(uint32_t* sz) GRPC_OVERRIDE {
+    *sz = call_.max_receive_message_size();
+    return true;
+  }
+
   bool Read(R* msg) GRPC_OVERRIDE {
     CallOpSet<CallOpRecvInitialMetadata, CallOpRecvMessage<R>> ops;
     if (!context_->initial_metadata_received_) {
@@ -293,6 +309,11 @@
     cq_.Pluck(&ops);  // status ignored
   }
 
+  bool NextMessageSize(uint32_t* sz) GRPC_OVERRIDE {
+    *sz = call_.max_receive_message_size();
+    return true;
+  }
+
   bool Read(R* msg) GRPC_OVERRIDE {
     CallOpSet<CallOpRecvInitialMetadata, CallOpRecvMessage<R>> ops;
     if (!context_->initial_metadata_received_) {
@@ -336,12 +357,17 @@
   Call call_;
 };
 
+/// Server-side interface for streaming reads of message of type \a R.
 template <class R>
-class ServerReader GRPC_FINAL : public ReaderInterface<R> {
+class ServerReaderInterface : public ServerStreamingInterface,
+                              public ReaderInterface<R> {};
+
+template <class R>
+class ServerReader GRPC_FINAL : public ServerReaderInterface<R> {
  public:
   ServerReader(Call* call, ServerContext* ctx) : call_(call), ctx_(ctx) {}
 
-  void SendInitialMetadata() {
+  void SendInitialMetadata() GRPC_OVERRIDE {
     GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
 
     CallOpSet<CallOpSendInitialMetadata> ops;
@@ -355,6 +381,11 @@
     call_->cq()->Pluck(&ops);
   }
 
+  bool NextMessageSize(uint32_t* sz) GRPC_OVERRIDE {
+    *sz = call_->max_receive_message_size();
+    return true;
+  }
+
   bool Read(R* msg) GRPC_OVERRIDE {
     CallOpSet<CallOpRecvMessage<R>> ops;
     ops.RecvMessage(msg);
@@ -367,12 +398,17 @@
   ServerContext* const ctx_;
 };
 
+/// Server-side interface for streaming writes of message of type \a W.
 template <class W>
-class ServerWriter GRPC_FINAL : public WriterInterface<W> {
+class ServerWriterInterface : public ServerStreamingInterface,
+                              public WriterInterface<W> {};
+
+template <class W>
+class ServerWriter GRPC_FINAL : public ServerWriterInterface<W> {
  public:
   ServerWriter(Call* call, ServerContext* ctx) : call_(call), ctx_(ctx) {}
 
-  void SendInitialMetadata() {
+  void SendInitialMetadata() GRPC_OVERRIDE {
     GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
 
     CallOpSet<CallOpSendInitialMetadata> ops;
@@ -411,10 +447,17 @@
 
 /// Server-side interface for bi-directional streaming.
 template <class W, class R>
-class ServerReaderWriter GRPC_FINAL : public WriterInterface<W>,
-                                      public ReaderInterface<R> {
+class ServerReaderWriterInterface : public ServerStreamingInterface,
+                                    public WriterInterface<W>,
+                                    public ReaderInterface<R> {};
+
+// Actual implementation of bi-directional streaming
+namespace internal {
+template <class W, class R>
+class ServerReaderWriterBody GRPC_FINAL {
  public:
-  ServerReaderWriter(Call* call, ServerContext* ctx) : call_(call), ctx_(ctx) {}
+  ServerReaderWriterBody(Call* call, ServerContext* ctx)
+      : call_(call), ctx_(ctx) {}
 
   void SendInitialMetadata() {
     GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
@@ -430,15 +473,19 @@
     call_->cq()->Pluck(&ops);
   }
 
-  bool Read(R* msg) GRPC_OVERRIDE {
+  bool NextMessageSize(uint32_t* sz) {
+    *sz = call_->max_receive_message_size();
+    return true;
+  }
+
+  bool Read(R* msg) {
     CallOpSet<CallOpRecvMessage<R>> ops;
     ops.RecvMessage(msg);
     call_->PerformOps(&ops);
     return call_->cq()->Pluck(&ops) && ops.got_message;
   }
 
-  using WriterInterface<W>::Write;
-  bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE {
+  bool Write(const W& msg, const WriteOptions& options) {
     CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> ops;
     if (!ops.SendMessage(msg, options).ok()) {
       return false;
@@ -459,6 +506,76 @@
   Call* const call_;
   ServerContext* const ctx_;
 };
+}
+
+// class to represent the user API for a bidirectional streaming call
+template <class W, class R>
+class ServerReaderWriter GRPC_FINAL : public ServerReaderWriterInterface<W, R> {
+ public:
+  ServerReaderWriter(Call* call, ServerContext* ctx) : body_(call, ctx) {}
+
+  void SendInitialMetadata() GRPC_OVERRIDE { body_.SendInitialMetadata(); }
+
+  bool NextMessageSize(uint32_t* sz) GRPC_OVERRIDE {
+    return body_.NextMessageSize(sz);
+  }
+
+  bool Read(R* msg) GRPC_OVERRIDE { return body_.Read(msg); }
+
+  using WriterInterface<W>::Write;
+  bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE {
+    return body_.Write(msg, options);
+  }
+
+ private:
+  internal::ServerReaderWriterBody<W, R> body_;
+};
+
+/// A class to represent a flow-controlled unary call. This is something
+/// of a hybrid between conventional unary and streaming. This is invoked
+/// through a unary call on the client side, but the server responds to it
+/// as though it were a single-ping-pong streaming call. The server can use
+/// the \a NextMessageSize method to determine an upper-bound on the size of
+/// the message.
+/// A key difference relative to streaming: ServerUnaryStreamer
+///  must have exactly 1 Read and exactly 1 Write, in that order, to function
+/// correctly. Otherwise, the RPC is in error.
+template <class RequestType, class ResponseType>
+class ServerUnaryStreamer GRPC_FINAL
+    : public ServerReaderWriterInterface<ResponseType, RequestType> {
+ public:
+  ServerUnaryStreamer(Call* call, ServerContext* ctx)
+      : body_(call, ctx), read_done_(false), write_done_(false) {}
+
+  void SendInitialMetadata() GRPC_OVERRIDE { body_.SendInitialMetadata(); }
+
+  bool NextMessageSize(uint32_t* sz) GRPC_OVERRIDE {
+    return body_.NextMessageSize(sz);
+  }
+
+  bool Read(RequestType* request) GRPC_OVERRIDE {
+    if (read_done_) {
+      return false;
+    }
+    read_done_ = true;
+    return body_.Read(request);
+  }
+
+  using WriterInterface<ResponseType>::Write;
+  bool Write(const ResponseType& response,
+             const WriteOptions& options) GRPC_OVERRIDE {
+    if (write_done_ || !read_done_) {
+      return false;
+    }
+    write_done_ = true;
+    return body_.Write(response, options);
+  }
+
+ private:
+  internal::ServerReaderWriterBody<ResponseType, RequestType> body_;
+  bool read_done_;
+  bool write_done_;
+};
 
 }  // namespace grpc
 
diff --git a/include/grpc++/impl/codegen/thrift_serializer.h b/include/grpc++/impl/codegen/thrift_serializer.h
new file mode 100644
index 0000000..fd324a9
--- /dev/null
+++ b/include/grpc++/impl/codegen/thrift_serializer.h
@@ -0,0 +1,217 @@
+/*
+ *
+ * Copyright 2016, 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 GRPCXX_IMPL_CODEGEN_THRIFT_SERIALIZER_H
+#define GRPCXX_IMPL_CODEGEN_THRIFT_SERIALIZER_H
+
+#include <grpc/impl/codegen/byte_buffer_reader.h>
+#include <grpc/impl/codegen/slice.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/protocol/TCompactProtocol.h>
+#include <thrift/protocol/TProtocolException.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TTransportUtils.h>
+#include <boost/make_shared.hpp>
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+namespace apache {
+namespace thrift {
+namespace util {
+
+using apache::thrift::protocol::TBinaryProtocolT;
+using apache::thrift::protocol::TCompactProtocolT;
+using apache::thrift::protocol::TMessageType;
+using apache::thrift::protocol::TNetworkBigEndian;
+using apache::thrift::transport::TMemoryBuffer;
+using apache::thrift::transport::TBufferBase;
+using apache::thrift::transport::TTransport;
+
+template <typename Dummy, typename Protocol>
+class ThriftSerializer {
+ public:
+  ThriftSerializer()
+      : prepared_(false),
+        last_deserialized_(false),
+        serialize_version_(false) {}
+
+  virtual ~ThriftSerializer() {}
+
+  // Serialize the passed type into the internal buffer
+  // and returns a pointer to internal buffer and its size
+  template <typename T>
+  void Serialize(const T& fields, const uint8_t** serialized_buffer,
+                 size_t* serialized_len) {
+    // prepare or reset buffer
+    if (!prepared_ || last_deserialized_) {
+      prepare();
+    } else {
+      buffer_->resetBuffer();
+    }
+    last_deserialized_ = false;
+
+    // if required serialize protocol version
+    if (serialize_version_) {
+      protocol_->writeMessageBegin("", TMessageType(0), 0);
+    }
+
+    // serialize fields into buffer
+    fields.write(protocol_.get());
+
+    // write the end of message
+    if (serialize_version_) {
+      protocol_->writeMessageEnd();
+    }
+
+    uint8_t* byte_buffer;
+    uint32_t byte_buffer_size;
+    buffer_->getBuffer(&byte_buffer, &byte_buffer_size);
+    *serialized_buffer = byte_buffer;
+    *serialized_len = byte_buffer_size;
+  }
+
+  // Serialize the passed type into the byte buffer
+  template <typename T>
+  void Serialize(const T& fields, grpc_byte_buffer** bp) {
+    const uint8_t* byte_buffer;
+    size_t byte_buffer_size;
+
+    Serialize(fields, &byte_buffer, &byte_buffer_size);
+
+    gpr_slice slice = gpr_slice_from_copied_buffer(
+        reinterpret_cast<const char*>(byte_buffer), byte_buffer_size);
+
+    *bp = grpc_raw_byte_buffer_create(&slice, 1);
+
+    gpr_slice_unref(slice);
+  }
+
+  // Deserialize the passed char array into  the passed type, returns the number
+  // of bytes that have been consumed from the passed string.
+  template <typename T>
+  uint32_t Deserialize(uint8_t* serialized_buffer, size_t length, T* fields) {
+    // prepare buffer if necessary
+    if (!prepared_) {
+      prepare();
+    }
+    last_deserialized_ = true;
+
+    // reset buffer transport
+    buffer_->resetBuffer(serialized_buffer, length);
+
+    // read the protocol version if necessary
+    if (serialize_version_) {
+      std::string name = "";
+      TMessageType mt = static_cast<TMessageType>(0);
+      int32_t seq_id = 0;
+      protocol_->readMessageBegin(name, mt, seq_id);
+    }
+
+    // deserialize buffer into fields
+    uint32_t len = fields->read(protocol_.get());
+
+    // read the end of message
+    if (serialize_version_) {
+      protocol_->readMessageEnd();
+    }
+
+    return len;
+  }
+
+  // Deserialize the passed byte buffer to passed type, returns the number
+  // of bytes consumed from byte buffer
+  template <typename T>
+  uint32_t Deserialize(grpc_byte_buffer* buffer, T* msg) {
+    grpc_byte_buffer_reader reader;
+    grpc_byte_buffer_reader_init(&reader, buffer);
+
+    gpr_slice slice = grpc_byte_buffer_reader_readall(&reader);
+
+    uint32_t len =
+        Deserialize(GPR_SLICE_START_PTR(slice), GPR_SLICE_LENGTH(slice), msg);
+
+    gpr_slice_unref(slice);
+
+    grpc_byte_buffer_reader_destroy(&reader);
+
+    return len;
+  }
+
+  // set serialization version flag
+  void SetSerializeVersion(bool value) { serialize_version_ = value; }
+
+  // Set the container size limit to deserialize
+  // This function should be called after buffer_ is initialized
+  void SetContainerSizeLimit(int32_t container_limit) {
+    if (!prepared_) {
+      prepare();
+    }
+    protocol_->setContainerSizeLimit(container_limit);
+  }
+
+  // Set the string size limit to deserialize
+  // This function should be called after buffer_ is initialized
+  void SetStringSizeLimit(int32_t string_limit) {
+    if (!prepared_) {
+      prepare();
+    }
+    protocol_->setStringSizeLimit(string_limit);
+  }
+
+ private:
+  bool prepared_;
+  bool last_deserialized_;
+  boost::shared_ptr<TMemoryBuffer> buffer_;
+  std::shared_ptr<Protocol> protocol_;
+  bool serialize_version_;
+
+  void prepare() {
+    buffer_ = boost::make_shared<TMemoryBuffer>();
+    // create a protocol for the memory buffer transport
+    protocol_ = std::make_shared<Protocol>(buffer_);
+    prepared_ = true;
+  }
+
+};  // ThriftSerializer
+
+typedef ThriftSerializer<void, TBinaryProtocolT<TBufferBase, TNetworkBigEndian>>
+    ThriftSerializerBinary;
+typedef ThriftSerializer<void, TCompactProtocolT<TBufferBase>>
+    ThriftSerializerCompact;
+
+}  // namespace util
+}  // namespace thrift
+}  // namespace apache
+
+#endif
diff --git a/include/grpc++/impl/codegen/thrift_utils.h b/include/grpc++/impl/codegen/thrift_utils.h
new file mode 100644
index 0000000..742d739
--- /dev/null
+++ b/include/grpc++/impl/codegen/thrift_utils.h
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2016, 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 GRPCXX_IMPL_CODEGEN_THRIFT_UTILS_H
+#define GRPCXX_IMPL_CODEGEN_THRIFT_UTILS_H
+
+#include <grpc++/impl/codegen/config.h>
+#include <grpc++/impl/codegen/core_codegen_interface.h>
+#include <grpc++/impl/codegen/serialization_traits.h>
+#include <grpc++/impl/codegen/status.h>
+#include <grpc++/impl/codegen/status_code_enum.h>
+#include <grpc++/impl/codegen/thrift_serializer.h>
+#include <grpc/impl/codegen/byte_buffer_reader.h>
+#include <grpc/impl/codegen/slice.h>
+#include <cstdint>
+#include <cstdlib>
+
+namespace grpc {
+
+using apache::thrift::util::ThriftSerializerCompact;
+
+template <class T>
+class SerializationTraits<T, typename std::enable_if<std::is_base_of<
+                                 apache::thrift::TBase, T>::value>::type> {
+ public:
+  static Status Serialize(const T& msg, grpc_byte_buffer** bp,
+                          bool* own_buffer) {
+    *own_buffer = true;
+
+    ThriftSerializerCompact serializer;
+    serializer.Serialize(msg, bp);
+
+    return Status(StatusCode::OK, "ok");
+  }
+
+  static Status Deserialize(grpc_byte_buffer* buffer, T* msg,
+                            int max_receive_message_size) {
+    if (!buffer) {
+      return Status(StatusCode::INTERNAL, "No payload");
+    }
+
+    ThriftSerializerCompact deserializer;
+    deserializer.Deserialize(buffer, msg);
+
+    grpc_byte_buffer_destroy(buffer);
+
+    return Status(StatusCode::OK, "ok");
+  }
+};
+
+}  // namespace grpc
+
+#endif  // GRPCXX_IMPL_CODEGEN_THRIFT_UTILS_H
diff --git a/include/grpc++/impl/codegen/time.h b/include/grpc++/impl/codegen/time.h
index d9b19bc..87c5112 100644
--- a/include/grpc++/impl/codegen/time.h
+++ b/include/grpc++/impl/codegen/time.h
@@ -35,7 +35,7 @@
 #define GRPCXX_IMPL_CODEGEN_TIME_H
 
 #include <grpc++/impl/codegen/config.h>
-#include <grpc/impl/codegen/time.h>
+#include <grpc/impl/codegen/grpc_types.h>
 
 namespace grpc {
 
@@ -79,7 +79,7 @@
 
 #include <chrono>
 
-#include <grpc/impl/codegen/time.h>
+#include <grpc/impl/codegen/grpc_types.h>
 
 namespace grpc {
 
diff --git a/include/grpc++/impl/server_builder_plugin.h b/include/grpc++/impl/server_builder_plugin.h
index 1e157ef..61632e3 100644
--- a/include/grpc++/impl/server_builder_plugin.h
+++ b/include/grpc++/impl/server_builder_plugin.h
@@ -41,6 +41,7 @@
 namespace grpc {
 
 class ServerInitializer;
+class ChannelArguments;
 
 class ServerBuilderPlugin {
  public:
@@ -58,6 +59,10 @@
   // ServerBuilderOption::UpdatePlugins
   virtual void ChangeArguments(const grpc::string& name, void* value) = 0;
 
+  // UpdateChannelArguments will be called in ServerBuilder::BuildAndStart(),
+  // before the Server instance is created.
+  virtual void UpdateChannelArguments(ChannelArguments* args) {}
+
   virtual bool has_sync_methods() const { return false; }
   virtual bool has_async_methods() const { return false; }
 };
diff --git a/include/grpc++/server.h b/include/grpc++/server.h
index 6876961..f51a6c6 100644
--- a/include/grpc++/server.h
+++ b/include/grpc++/server.h
@@ -96,9 +96,6 @@
   // Returns a \em raw pointer to the underlying grpc_server instance.
   grpc_server* c_server();
 
-  // Returns a \em raw pointer to the underlying CompletionQueue.
-  CompletionQueue* completion_queue();
-
  private:
   friend class AsyncGenericService;
   friend class ServerBuilder;
@@ -116,10 +113,10 @@
   ///
   /// \param thread_pool The threadpool instance to use for call processing.
   /// \param thread_pool_owned Does the server own the \a thread_pool instance?
-  /// \param max_message_size Maximum message length that the channel can
-  /// receive.
+  /// \param max_receive_message_size Maximum message length that the channel
+  /// can receive.
   Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned,
-         int max_message_size, ChannelArguments* args);
+         int max_receive_message_size, ChannelArguments* args);
 
   /// Register a service. This call does not take ownership of the service.
   /// The service must exist for the lifetime of the Server instance.
@@ -164,13 +161,15 @@
 
   void ShutdownInternal(gpr_timespec deadline) GRPC_OVERRIDE;
 
-  int max_message_size() const GRPC_OVERRIDE { return max_message_size_; };
+  int max_receive_message_size() const GRPC_OVERRIDE {
+    return max_receive_message_size_;
+  };
 
   grpc_server* server() GRPC_OVERRIDE { return server_; };
 
   ServerInitializer* initializer();
 
-  const int max_message_size_;
+  const int max_receive_message_size_;
 
   // Completion queue.
   CompletionQueue cq_;
diff --git a/include/grpc++/server_builder.h b/include/grpc++/server_builder.h
index b9c49f0..37f1f8c 100644
--- a/include/grpc++/server_builder.h
+++ b/include/grpc++/server_builder.h
@@ -78,12 +78,23 @@
   /// Only matches requests with :authority \a host
   ServerBuilder& RegisterService(const grpc::string& host, Service* service);
 
-  /// Set max message size in bytes.
-  ServerBuilder& SetMaxMessageSize(int max_message_size) {
-    max_message_size_ = max_message_size;
+  /// Set max receive message size in bytes.
+  ServerBuilder& SetMaxReceiveMessageSize(int max_receive_message_size) {
+    max_receive_message_size_ = max_receive_message_size;
     return *this;
   }
 
+  /// Set max send message size in bytes.
+  ServerBuilder& SetMaxSendMessageSize(int max_send_message_size) {
+    max_send_message_size_ = max_send_message_size;
+    return *this;
+  }
+
+  /// \deprecated For backward compatibility.
+  ServerBuilder& SetMaxMessageSize(int max_message_size) {
+    return SetMaxReceiveMessageSize(max_message_size);
+  }
+
   /// Set the support status for compression algorithms. All algorithms are
   /// enabled by default.
   ///
@@ -168,7 +179,8 @@
     Service* service;
   };
 
-  int max_message_size_;
+  int max_receive_message_size_;
+  int max_send_message_size_;
   std::vector<std::unique_ptr<ServerBuilderOption>> options_;
   std::vector<std::unique_ptr<NamedService>> services_;
   std::vector<Port> ports_;
diff --git a/include/grpc++/support/byte_buffer.h b/include/grpc++/support/byte_buffer.h
index 20bd407..06f8969 100644
--- a/include/grpc++/support/byte_buffer.h
+++ b/include/grpc++/support/byte_buffer.h
@@ -72,6 +72,9 @@
   /// Buffer size in bytes.
   size_t Length() const;
 
+  /// Swap the state of *this and *other.
+  void Swap(ByteBuffer* other);
+
  private:
   friend class SerializationTraits<ByteBuffer, void>;
 
@@ -93,7 +96,7 @@
 class SerializationTraits<ByteBuffer, void> {
  public:
   static Status Deserialize(grpc_byte_buffer* byte_buffer, ByteBuffer* dest,
-                            int max_message_size) {
+                            int max_receive_message_size) {
     dest->set_buffer(byte_buffer);
     return Status::OK;
   }
diff --git a/include/grpc/byte_buffer.h b/include/grpc/byte_buffer.h
index c3a3943..f25c1d5 100644
--- a/include/grpc/byte_buffer.h
+++ b/include/grpc/byte_buffer.h
@@ -34,6 +34,69 @@
 #ifndef GRPC_BYTE_BUFFER_H
 #define GRPC_BYTE_BUFFER_H
 
-#include <grpc/impl/codegen/byte_buffer.h>
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/slice_buffer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Returns a RAW byte buffer instance over the given slices (up to \a nslices).
+ *
+ * Increases the reference count for all \a slices processed. The user is
+ * responsible for invoking grpc_byte_buffer_destroy on the returned instance.*/
+GRPCAPI grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices,
+                                                      size_t nslices);
+
+/** Returns a *compressed* RAW byte buffer instance over the given slices (up to
+ * \a nslices). The \a compression argument defines the compression algorithm
+ * used to generate the data in \a slices.
+ *
+ * Increases the reference count for all \a slices processed. The user is
+ * responsible for invoking grpc_byte_buffer_destroy on the returned instance.*/
+GRPCAPI grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create(
+    gpr_slice *slices, size_t nslices, grpc_compression_algorithm compression);
+
+/** Copies input byte buffer \a bb.
+ *
+ * Increases the reference count of all the source slices. The user is
+ * responsible for calling grpc_byte_buffer_destroy over the returned copy. */
+GRPCAPI grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb);
+
+/** Returns the size of the given byte buffer, in bytes. */
+GRPCAPI size_t grpc_byte_buffer_length(grpc_byte_buffer *bb);
+
+/** Destroys \a byte_buffer deallocating all its memory. */
+GRPCAPI void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer);
+
+/** Reader for byte buffers. Iterates over slices in the byte buffer */
+struct grpc_byte_buffer_reader;
+typedef struct grpc_byte_buffer_reader grpc_byte_buffer_reader;
+
+/** Initialize \a reader to read over \a buffer.
+ * Returns 1 upon success, 0 otherwise. */
+GRPCAPI int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
+                                         grpc_byte_buffer *buffer);
+
+/** Cleanup and destroy \a reader */
+GRPCAPI void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader);
+
+/** Updates \a slice with the next piece of data from from \a reader and returns
+ * 1. Returns 0 at the end of the stream. Caller is responsible for calling
+ * gpr_slice_unref on the result. */
+GRPCAPI int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
+                                         gpr_slice *slice);
+
+/** Merge all data from \a reader into single slice */
+GRPCAPI gpr_slice
+grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader *reader);
+
+/** Returns a RAW byte buffer instance from the output of \a reader. */
+GRPCAPI grpc_byte_buffer *grpc_raw_byte_buffer_from_reader(
+    grpc_byte_buffer_reader *reader);
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* GRPC_BYTE_BUFFER_H */
diff --git a/include/grpc/census.h b/include/grpc/census.h
index 39d87ba..62ff45d 100644
--- a/include/grpc/census.h
+++ b/include/grpc/census.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -56,9 +56,11 @@
 
 /** Shutdown and startup census subsystem. The 'features' argument should be
  * the OR (|) of census_features values. If census fails to initialize, then
- * census_initialize() will return a non-zero value. It is an error to call
- * census_initialize() more than once (without an intervening
- * census_shutdown()). */
+ * census_initialize() will return -1, otherwise the set of enabled features
+ * (which may be smaller than that provided in the `features` argument, see
+ * census_supported()) is returned. It is an error to call census_initialize()
+ * more than once (without an intervening census_shutdown()). These functions
+ * are not thread-safe. */
 CENSUSAPI int census_initialize(int features);
 CENSUSAPI void census_shutdown(void);
 
@@ -430,30 +432,44 @@
 CENSUSAPI void census_trace_scan_end();
 
 /* Core stats collection API's. The following concepts are used:
-   * Aggregation: A collection of values. Census supports the following
-       aggregation types:
-         Sum - a single summation type. Typically used for keeping (e.g.)
-           counts of events.
-         Distribution - statistical distribution information, used for
-           recording average, standard deviation etc.
-         Histogram - a histogram of measurements falling in defined bucket
-           boundaries.
-         Window - a count of events that happen in reolling time window.
-     New aggregation types can be added by the user, if desired (see
-     census_register_aggregation()).
-   * Metric: Each measurement is for a single metric. Examples include RPC
-     latency, CPU seconds consumed, and bytes transmitted.
-   * View: A view is a combination of a metric, a tag set (in which the tag
-     values are regular expressions) and a set of aggregations. When a
-     measurement for a metric matches the view tags, it is recorded (for each
-     unique set of tags) against each aggregation. Each metric can have an
-     arbitrary number of views by which it will be broken down.
+   * Resource: Users record measurements for a single resource. Examples
+     include RPC latency, CPU seconds consumed, and bytes transmitted.
+   * Aggregation: An aggregation of a set of measurements. Census supports the
+       following aggregation types:
+       * Distribution - statistical distribution information, used for
+         recording average, standard deviation etc. Can include a histogram.
+       * Interval - a count of events that happen in a rolling time window.
+   * View: A view is a combination of a Resource, a set of tag keys and an
+     Aggregation. When a measurement for a Resource matches the View tags, it is
+     recorded (for each unique set of tag values) using the Aggregation type.
+     Each resource can have an arbitrary number of views by which it will be
+     broken down.
+
+  Census uses protos to define each of the above, and output results. This
+  ensures unification across the different language and runtime
+  implementations. The proto definitions can be found in src/proto/census.
 */
 
+/* Define a new resource. `resource_pb` should contain an encoded Resource
+   protobuf, `resource_pb_size` being the size of the buffer. Returns a -ve
+   value on error, or a positive (>= 0) resource id (for use in
+   census_delete_resource() and census_record_values()). In order to be valid, a
+   resource must have a name, and at least one numerator in it's unit type. The
+   resource name must be unique, and an error will be returned if it is not. */
+CENSUSAPI int32_t census_define_resource(const uint8_t *resource_pb,
+                                         size_t resource_pb_size);
+
+/* Delete a resource created by census_define_resource(). */
+CENSUSAPI void census_delete_resource(int32_t resource_id);
+
+/* Determine the id of a resource, given it's name. returns -1 if the resource
+   does not exist. */
+CENSUSAPI int32_t census_resource_id(const char *name);
+
 /* A single value to be recorded comprises two parts: an ID for the particular
- * metric and the value to be recorded against it. */
+ * resource and the value to be recorded against it. */
 typedef struct {
-  uint32_t metric_id;
+  int32_t resource_id;
   double value;
 } census_value;
 
@@ -461,78 +477,6 @@
 CENSUSAPI void census_record_values(census_context *context,
                                     census_value *values, size_t nvalues);
 
-/** Type representing a particular aggregation */
-typedef struct census_aggregation_ops census_aggregation_ops;
-
-/* Predefined aggregation types, for use with census_view_create(). */
-extern census_aggregation_ops census_agg_sum;
-extern census_aggregation_ops census_agg_distribution;
-extern census_aggregation_ops census_agg_histogram;
-extern census_aggregation_ops census_agg_window;
-
-/** Information needed to instantiate a new aggregation. Used in view
-    construction via census_define_view(). */
-typedef struct {
-  const census_aggregation_ops *ops;
-  const void *create_arg; /* Aaggregation initialization argument. */
-} census_aggregation;
-
-/** A census view type. Opaque. */
-typedef struct census_view census_view;
-
-/** Create a new view.
-  @param metric_id Metric with which this view is associated.
-  @param tags tags that define the view.
-  @param aggregations aggregations to associate with the view
-  @param naggregations number of aggregations
-
-  @return A new census view
-*/
-
-/* TODO(aveitch): consider if context is the right argument type to pass in
-   tags. */
-CENSUSAPI census_view *census_view_create(
-    uint32_t metric_id, const census_context *tags,
-    const census_aggregation *aggregations, size_t naggregations);
-
-/** Destroy a previously created view. */
-CENSUSAPI void census_view_delete(census_view *view);
-
-/** Metric ID associated with a view */
-CENSUSAPI size_t census_view_metric(const census_view *view);
-
-/** Number of aggregations associated with view. */
-CENSUSAPI size_t census_view_naggregations(const census_view *view);
-
-/** Get tags associated with view. */
-CENSUSAPI const census_context *census_view_tags(const census_view *view);
-
-/** Get aggregation descriptors associated with a view. */
-CENSUSAPI const census_aggregation *census_view_aggregrations(
-    const census_view *view);
-
-/** Holds all the aggregation data for a particular view instantiation. Forms
-  part of the data returned by census_view_data(). */
-typedef struct {
-  const census_context *tags; /* Tags for this set of aggregations. */
-  const void **data; /* One data set for every aggregation in the view. */
-} census_view_aggregation_data;
-
-/** Census view data as returned by census_view_get_data(). */
-typedef struct {
-  size_t n_tag_sets; /* Number of unique tag sets that matched view. */
-  const census_view_aggregation_data *data; /* n_tag_sets entries */
-} census_view_data;
-
-/** Get data from aggregations associated with a view.
-  @param view View from which to get data.
-  @return Full set of data for all aggregations for the view.
-*/
-CENSUSAPI const census_view_data *census_view_get_data(const census_view *view);
-
-/** Reset all view data to zero for the specified view */
-CENSUSAPI void census_view_reset(census_view *view);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/grpc/compression.h b/include/grpc/compression.h
index 22bcf0e..5f285cd 100644
--- a/include/grpc/compression.h
+++ b/include/grpc/compression.h
@@ -36,9 +36,10 @@
 
 #include <stdlib.h>
 
-#include <grpc/impl/codegen/compression_types.h>
 #include <grpc/impl/codegen/port_platform.h>
 
+#include <grpc/impl/codegen/compression_types.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index 6f7a67b..587d86c 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -90,6 +90,9 @@
 /** Return a string representing the current version of grpc */
 GRPCAPI const char *grpc_version_string(void);
 
+/** Return a string specifying what the 'g' in gRPC stands for */
+GRPCAPI const char *grpc_g_stands_for(void);
+
 /** Create a completion queue */
 GRPCAPI grpc_completion_queue *grpc_completion_queue_create(void *reserved);
 
@@ -170,8 +173,9 @@
     completions are sent to 'completion_queue'. 'method' and 'host' need only
     live through the invocation of this function.
     If parent_call is non-NULL, it must be a server-side call. It will be used
-    to propagate properties from the server call to this new client call.
-    */
+    to propagate properties from the server call to this new client call,
+    depending on the value of \a propagation_mask (see propagation_bits.h for
+    possible values). */
 GRPCAPI grpc_call *grpc_channel_create_call(
     grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask,
     grpc_completion_queue *completion_queue, const char *method,
@@ -187,7 +191,8 @@
                                          const char *method, const char *host,
                                          void *reserved);
 
-/** Create a call given a handle returned from grpc_channel_register_call */
+/** Create a call given a handle returned from grpc_channel_register_call.
+    \sa grpc_channel_create_call. */
 GRPCAPI grpc_call *grpc_channel_create_registered_call(
     grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask,
     grpc_completion_queue *completion_queue, void *registered_call_handle,
diff --git a/include/grpc/grpc_posix.h b/include/grpc/grpc_posix.h
index 5e89ae3..a8c0315 100644
--- a/include/grpc/grpc_posix.h
+++ b/include/grpc/grpc_posix.h
@@ -57,14 +57,17 @@
 
 /** Add the connected communication channel based on file descriptor 'fd' to the
     'server'. The 'fd' must be an open file descriptor corresponding to a
-    connected socket. The 'cq' is a completion queue that will be getting events
-    from that descriptor. */
+    connected socket. Events from the file descriptor may come on any of the
+    server completion queues (i.e completion queues registered via the
+    grpc_server_register_completion_queue API).
+
+    The 'reserved' pointer MUST be NULL.
+    */
 GRPCAPI void grpc_server_add_insecure_channel_from_fd(grpc_server *server,
-                                                      grpc_completion_queue *cq,
-                                                      int fd);
+                                                      void *reserved, int fd);
 
 /** GRPC Core POSIX library may internally use signals to optimize some work.
-   The library uses (SIGRTMIN + 2) signal by default. Use this API to instruct
+   The library uses (SIGRTMIN + 6) signal by default. Use this API to instruct
    the library to use a different signal i.e 'signum' instead.
    Note:
    - To prevent GRPC library from using any signals, pass a 'signum' of -1
diff --git a/include/grpc/impl/codegen/alloc.h b/include/grpc/impl/codegen/alloc.h
deleted file mode 100644
index 683c755..0000000
--- a/include/grpc/impl/codegen/alloc.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef GRPC_IMPL_CODEGEN_ALLOC_H
-#define GRPC_IMPL_CODEGEN_ALLOC_H
-
-#include <stddef.h>
-
-#include <grpc/impl/codegen/port_platform.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct gpr_allocation_functions {
-  void *(*malloc_fn)(size_t size);
-  void *(*realloc_fn)(void *ptr, size_t size);
-  void (*free_fn)(void *ptr);
-} gpr_allocation_functions;
-
-/* malloc, never returns NULL */
-GPRAPI void *gpr_malloc(size_t size);
-/* free */
-GPRAPI void gpr_free(void *ptr);
-/* realloc, never returns NULL */
-GPRAPI void *gpr_realloc(void *p, size_t size);
-/* aligned malloc, never returns NULL, will align to 1 << alignment_log */
-GPRAPI void *gpr_malloc_aligned(size_t size, size_t alignment_log);
-/* free memory allocated by gpr_malloc_aligned */
-GPRAPI void gpr_free_aligned(void *ptr);
-
-/** Request the family of allocation functions in \a functions be used. NOTE
- * that this request will be honored in a *best effort* basis and that no
- * guarantees are made about the default functions (eg, malloc) being called. */
-GPRAPI void gpr_set_allocation_functions(gpr_allocation_functions functions);
-
-/** Return the family of allocation functions currently in effect. */
-GPRAPI gpr_allocation_functions gpr_get_allocation_functions();
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* GRPC_IMPL_CODEGEN_ALLOC_H */
diff --git a/include/grpc/impl/codegen/atm.h b/include/grpc/impl/codegen/atm.h
index 5589d5d..ae00fb0 100644
--- a/include/grpc/impl/codegen/atm.h
+++ b/include/grpc/impl/codegen/atm.h
@@ -75,6 +75,9 @@
    int gpr_atm_no_barrier_cas(gpr_atm *p, gpr_atm o, gpr_atm n);
    int gpr_atm_acq_cas(gpr_atm *p, gpr_atm o, gpr_atm n);
    int gpr_atm_rel_cas(gpr_atm *p, gpr_atm o, gpr_atm n);
+
+   // Atomically, set *p=n and return the old value of *p
+   gpr_atm gpr_atm_full_xchg(gpr_atm *p, gpr_atm n);
 */
 
 #include <grpc/impl/codegen/port_platform.h>
diff --git a/include/grpc/impl/codegen/atm_gcc_atomic.h b/include/grpc/impl/codegen/atm_gcc_atomic.h
index 8caf7ed..7d4ae98 100644
--- a/include/grpc/impl/codegen/atm_gcc_atomic.h
+++ b/include/grpc/impl/codegen/atm_gcc_atomic.h
@@ -69,4 +69,6 @@
                                      __ATOMIC_RELAXED);
 }
 
+#define gpr_atm_full_xchg(p, n) __atomic_exchange_n((p), (n), __ATOMIC_ACQ_REL)
+
 #endif /* GRPC_IMPL_CODEGEN_ATM_GCC_ATOMIC_H */
diff --git a/include/grpc/impl/codegen/atm_gcc_sync.h b/include/grpc/impl/codegen/atm_gcc_sync.h
index 915b09f..9aa2b43 100644
--- a/include/grpc/impl/codegen/atm_gcc_sync.h
+++ b/include/grpc/impl/codegen/atm_gcc_sync.h
@@ -84,4 +84,12 @@
 #define gpr_atm_acq_cas(p, o, n) (__sync_bool_compare_and_swap((p), (o), (n)))
 #define gpr_atm_rel_cas(p, o, n) gpr_atm_acq_cas((p), (o), (n))
 
+static __inline gpr_atm gpr_atm_full_xchg(gpr_atm *p, gpr_atm n) {
+  gpr_atm cur;
+  do {
+    cur = gpr_atm_acq_load(p);
+  } while (!gpr_atm_rel_cas(p, cur, n));
+  return cur;
+}
+
 #endif /* GRPC_IMPL_CODEGEN_ATM_GCC_SYNC_H */
diff --git a/include/grpc/impl/codegen/atm_windows.h b/include/grpc/impl/codegen/atm_windows.h
index d5fa8c0..0ab70b9 100644
--- a/include/grpc/impl/codegen/atm_windows.h
+++ b/include/grpc/impl/codegen/atm_windows.h
@@ -122,4 +122,8 @@
   return old;
 }
 
+static __inline gpr_atm gpr_atm_full_xchg(gpr_atm *p, gpr_atm n) {
+  return (gpr_atm)InterlockedExchangePointer((PVOID *)p, (PVOID)n);
+}
+
 #endif /* GRPC_IMPL_CODEGEN_ATM_WINDOWS_H */
diff --git a/include/grpc/impl/codegen/byte_buffer.h b/include/grpc/impl/codegen/byte_buffer.h
deleted file mode 100644
index fe1e215..0000000
--- a/include/grpc/impl/codegen/byte_buffer.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- *
- * Copyright 2015-2016, 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_IMPL_CODEGEN_BYTE_BUFFER_H
-#define GRPC_IMPL_CODEGEN_BYTE_BUFFER_H
-
-#include <grpc/impl/codegen/compression_types.h>
-#include <grpc/impl/codegen/slice_buffer.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
-  GRPC_BB_RAW
-  /* Future types may include GRPC_BB_PROTOBUF, etc. */
-} grpc_byte_buffer_type;
-
-struct grpc_byte_buffer {
-  void *reserved;
-  grpc_byte_buffer_type type;
-  union {
-    struct {
-      void *reserved[8];
-    } reserved;
-    struct {
-      grpc_compression_algorithm compression;
-      gpr_slice_buffer slice_buffer;
-    } raw;
-  } data;
-};
-typedef struct grpc_byte_buffer grpc_byte_buffer;
-
-/** Returns a RAW byte buffer instance over the given slices (up to \a nslices).
- *
- * Increases the reference count for all \a slices processed. The user is
- * responsible for invoking grpc_byte_buffer_destroy on the returned instance.*/
-GRPCAPI grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices,
-                                                      size_t nslices);
-
-/** Returns a *compressed* RAW byte buffer instance over the given slices (up to
- * \a nslices). The \a compression argument defines the compression algorithm
- * used to generate the data in \a slices.
- *
- * Increases the reference count for all \a slices processed. The user is
- * responsible for invoking grpc_byte_buffer_destroy on the returned instance.*/
-GRPCAPI grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create(
-    gpr_slice *slices, size_t nslices, grpc_compression_algorithm compression);
-
-/** Copies input byte buffer \a bb.
- *
- * Increases the reference count of all the source slices. The user is
- * responsible for calling grpc_byte_buffer_destroy over the returned copy. */
-GRPCAPI grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb);
-
-/** Returns the size of the given byte buffer, in bytes. */
-GRPCAPI size_t grpc_byte_buffer_length(grpc_byte_buffer *bb);
-
-/** Destroys \a byte_buffer deallocating all its memory. */
-GRPCAPI void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer);
-
-/** Reader for byte buffers. Iterates over slices in the byte buffer */
-struct grpc_byte_buffer_reader;
-typedef struct grpc_byte_buffer_reader grpc_byte_buffer_reader;
-
-/** Initialize \a reader to read over \a buffer.
- * Returns 1 upon success, 0 otherwise. */
-GRPCAPI int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
-                                         grpc_byte_buffer *buffer);
-
-/** Cleanup and destroy \a reader */
-GRPCAPI void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader);
-
-/** Updates \a slice with the next piece of data from from \a reader and returns
- * 1. Returns 0 at the end of the stream. Caller is responsible for calling
- * gpr_slice_unref on the result. */
-GRPCAPI int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
-                                         gpr_slice *slice);
-
-/** Merge all data from \a reader into single slice */
-GRPCAPI gpr_slice
-grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader *reader);
-
-/** Returns a RAW byte buffer instance from the output of \a reader. */
-GRPCAPI grpc_byte_buffer *grpc_raw_byte_buffer_from_reader(
-    grpc_byte_buffer_reader *reader);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* GRPC_IMPL_CODEGEN_BYTE_BUFFER_H */
diff --git a/include/grpc/impl/codegen/byte_buffer_reader.h b/include/grpc/impl/codegen/byte_buffer_reader.h
index 10c3829..85a4864 100644
--- a/include/grpc/impl/codegen/byte_buffer_reader.h
+++ b/include/grpc/impl/codegen/byte_buffer_reader.h
@@ -34,15 +34,15 @@
 #ifndef GRPC_IMPL_CODEGEN_BYTE_BUFFER_READER_H
 #define GRPC_IMPL_CODEGEN_BYTE_BUFFER_READER_H
 
-#include <grpc/impl/codegen/byte_buffer.h>
-
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+struct grpc_byte_buffer;
+
 struct grpc_byte_buffer_reader {
-  grpc_byte_buffer *buffer_in;
-  grpc_byte_buffer *buffer_out;
+  struct grpc_byte_buffer *buffer_in;
+  struct grpc_byte_buffer *buffer_out;
   /* Different current objects correspond to different types of byte buffers */
   union {
     /* Index into a slice buffer's array of slices */
diff --git a/include/grpc/impl/codegen/compression_types.h b/include/grpc/impl/codegen/compression_types.h
index 3034182..170d99f 100644
--- a/include/grpc/impl/codegen/compression_types.h
+++ b/include/grpc/impl/codegen/compression_types.h
@@ -34,8 +34,8 @@
 #ifndef GRPC_IMPL_CODEGEN_COMPRESSION_TYPES_H
 #define GRPC_IMPL_CODEGEN_COMPRESSION_TYPES_H
 
-#include <grpc/impl/codegen/port_platform.h>
 #include <stdbool.h>
+#include <stdint.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/include/grpc/impl/codegen/gpr_types.h b/include/grpc/impl/codegen/gpr_types.h
new file mode 100644
index 0000000..c8f0815
--- /dev/null
+++ b/include/grpc/impl/codegen/gpr_types.h
@@ -0,0 +1,140 @@
+/*
+ *
+ * Copyright 2016, 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_IMPL_CODEGEN_GPR_TYPES_H
+#define GRPC_IMPL_CODEGEN_GPR_TYPES_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The clocks we support. */
+typedef enum {
+  /* Monotonic clock. Epoch undefined. Always moves forwards. */
+  GPR_CLOCK_MONOTONIC = 0,
+  /* Realtime clock. May jump forwards or backwards. Settable by
+     the system administrator. Has its epoch at 0:00:00 UTC 1 Jan 1970. */
+  GPR_CLOCK_REALTIME,
+  /* CPU cycle time obtained by rdtsc instruction on x86 platforms. Epoch
+     undefined. Degrades to GPR_CLOCK_REALTIME on other platforms. */
+  GPR_CLOCK_PRECISE,
+  /* Unmeasurable clock type: no base, created by taking the difference
+     between two times */
+  GPR_TIMESPAN
+} gpr_clock_type;
+
+/* Analogous to struct timespec. On some machines, absolute times may be in
+ * local time. */
+typedef struct gpr_timespec {
+  int64_t tv_sec;
+  int32_t tv_nsec;
+  /** Against which clock was this time measured? (or GPR_TIMESPAN if
+      this is a relative time meaure) */
+  gpr_clock_type clock_type;
+} gpr_timespec;
+
+/* Slice API
+
+   A slice represents a contiguous reference counted array of bytes.
+   It is cheap to take references to a slice, and it is cheap to create a
+   slice pointing to a subset of another slice.
+
+   The data-structure for slices is exposed here to allow non-gpr code to
+   build slices from whatever data they have available.
+
+   When defining interfaces that handle slices, care should be taken to define
+   reference ownership semantics (who should call unref?) and mutability
+   constraints (is the callee allowed to modify the slice?) */
+
+/* Reference count container for gpr_slice. Contains function pointers to
+   increment and decrement reference counts. Implementations should cleanup
+   when the reference count drops to zero.
+   Typically client code should not touch this, and use gpr_slice_malloc,
+   gpr_slice_new, or gpr_slice_new_with_len instead. */
+typedef struct gpr_slice_refcount {
+  void (*ref)(void *);
+  void (*unref)(void *);
+} gpr_slice_refcount;
+
+#define GPR_SLICE_INLINED_SIZE (sizeof(size_t) + sizeof(uint8_t *) - 1)
+
+/* A gpr_slice s, if initialized, represents the byte range
+   s.bytes[0..s.length-1].
+
+   It can have an associated ref count which has a destruction routine to be run
+   when the ref count reaches zero (see gpr_slice_new() and grp_slice_unref()).
+   Multiple gpr_slice values may share a ref count.
+
+   If the slice does not have a refcount, it represents an inlined small piece
+   of data that is copied by value. */
+typedef struct gpr_slice {
+  struct gpr_slice_refcount *refcount;
+  union {
+    struct {
+      uint8_t *bytes;
+      size_t length;
+    } refcounted;
+    struct {
+      uint8_t length;
+      uint8_t bytes[GPR_SLICE_INLINED_SIZE];
+    } inlined;
+  } data;
+} gpr_slice;
+
+#define GRPC_SLICE_BUFFER_INLINE_ELEMENTS 8
+
+/* Represents an expandable array of slices, to be interpreted as a
+   single item. */
+typedef struct {
+  /* slices in the array */
+  gpr_slice *slices;
+  /* the number of slices in the array */
+  size_t count;
+  /* the number of slices allocated in the array */
+  size_t capacity;
+  /* the combined length of all slices in the array */
+  size_t length;
+  /* inlined elements to avoid allocations */
+  gpr_slice inlined[GRPC_SLICE_BUFFER_INLINE_ELEMENTS];
+} gpr_slice_buffer;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_GPR_TYPES_H */
diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h
index e5a8288..07e7cff 100644
--- a/include/grpc/impl/codegen/grpc_types.h
+++ b/include/grpc/impl/codegen/grpc_types.h
@@ -34,15 +34,37 @@
 #ifndef GRPC_IMPL_CODEGEN_GRPC_TYPES_H
 #define GRPC_IMPL_CODEGEN_GRPC_TYPES_H
 
-#include <grpc/impl/codegen/byte_buffer.h>
+#include <grpc/impl/codegen/gpr_types.h>
+
+#include <grpc/impl/codegen/compression_types.h>
 #include <grpc/impl/codegen/status.h>
 
 #include <stddef.h>
+#include <stdint.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+typedef enum {
+  GRPC_BB_RAW
+  /* Future types may include GRPC_BB_PROTOBUF, etc. */
+} grpc_byte_buffer_type;
+
+typedef struct grpc_byte_buffer {
+  void *reserved;
+  grpc_byte_buffer_type type;
+  union {
+    struct {
+      void *reserved[8];
+    } reserved;
+    struct {
+      grpc_compression_algorithm compression;
+      gpr_slice_buffer slice_buffer;
+    } raw;
+  } data;
+} grpc_byte_buffer;
+
 /** Completion Queues enable notification of the completion of asynchronous
     actions. */
 typedef struct grpc_completion_queue grpc_completion_queue;
@@ -126,8 +148,14 @@
 /** Maximum number of concurrent incoming streams to allow on a http2
     connection. Int valued. */
 #define GRPC_ARG_MAX_CONCURRENT_STREAMS "grpc.max_concurrent_streams"
-/** Maximum message length that the channel can receive. Int valued, bytes. */
-#define GRPC_ARG_MAX_MESSAGE_LENGTH "grpc.max_message_length"
+/** Maximum message length that the channel can receive. Int valued, bytes.
+    -1 means unlimited. */
+#define GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH "grpc.max_receive_message_length"
+/** \deprecated For backward compatibility. */
+#define GRPC_ARG_MAX_MESSAGE_LENGTH GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH
+/** Maximum message length that the channel can send. Int valued, bytes.
+    -1 means unlimited. */
+#define GRPC_ARG_MAX_SEND_MESSAGE_LENGTH "grpc.max_send_message_length"
 /** Initial sequence number for http2 transports. Int valued. */
 #define GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER \
   "grpc.http2.initial_sequence_number"
@@ -142,6 +170,11 @@
 /** How much memory to use for hpack encoding. Int valued, bytes. */
 #define GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER \
   "grpc.http2.hpack_table_size.encoder"
+/** How big a frame are we willing to receive via HTTP2.
+    Min 16384, max 16777215.
+    Larger values give lower CPU usage for large messages, but more head of line
+    blocking for small messages. */
+#define GRPC_ARG_HTTP2_MAX_FRAME_SIZE "grpc.http2.max_frame_size"
 /** Default authority to pass if none specified on call construction. A string.
  * */
 #define GRPC_ARG_DEFAULT_AUTHORITY "grpc.default_authority"
@@ -153,6 +186,9 @@
 #define GRPC_ARG_SECONDARY_USER_AGENT_STRING "grpc.secondary_user_agent"
 /** The maximum time between subsequent connection attempts, in ms */
 #define GRPC_ARG_MAX_RECONNECT_BACKOFF_MS "grpc.max_reconnect_backoff_ms"
+/** The time between the first and second connection attempts, in ms */
+#define GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS \
+  "grpc.initial_reconnect_backoff_ms"
 /* The caller of the secure_channel_create functions may override the target
    name used for SSL host name checking using this channel argument which is of
    type \a GRPC_ARG_STRING. This *should* be used for testing only.
@@ -222,10 +258,14 @@
 #define GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST (0x00000010u)
 /** Signal that the call should not return UNAVAILABLE before it has started */
 #define GRPC_INITIAL_METADATA_IGNORE_CONNECTIVITY (0x00000020u)
+/** Signal that the call is cacheable. GRPC is free to use GET verb */
+#define GRPC_INITIAL_METADATA_CACHEABLE_REQUEST (0x00000040u)
+
 /** Mask of all valid flags */
-#define GRPC_INITIAL_METADATA_USED_MASK       \
-  (GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST | \
-   GRPC_INITIAL_METADATA_IGNORE_CONNECTIVITY)
+#define GRPC_INITIAL_METADATA_USED_MASK        \
+  (GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST |  \
+   GRPC_INITIAL_METADATA_IGNORE_CONNECTIVITY | \
+   GRPC_INITIAL_METADATA_CACHEABLE_REQUEST)
 
 /** A single metadata element */
 typedef struct grpc_metadata {
@@ -327,6 +367,8 @@
   GRPC_OP_RECV_CLOSE_ON_SERVER
 } grpc_op_type;
 
+struct grpc_byte_buffer;
+
 /** Operation data: one field for each op type (except SEND_CLOSE_FROM_CLIENT
    which has no arguments) */
 typedef struct grpc_op {
@@ -351,7 +393,7 @@
         grpc_compression_level level;
       } maybe_compression_level;
     } send_initial_metadata;
-    grpc_byte_buffer *send_message;
+    struct grpc_byte_buffer *send_message;
     struct {
       size_t trailing_metadata_count;
       grpc_metadata *trailing_metadata;
@@ -367,7 +409,7 @@
     /** ownership of the byte buffer is moved to the caller; the caller must
         call grpc_byte_buffer_destroy on this value, or reuse it in a future op.
        */
-    grpc_byte_buffer **recv_message;
+    struct grpc_byte_buffer **recv_message;
     struct {
       /** ownership of the array is with the caller, but ownership of the
           elements stays with the call object (ie key, value members are owned
diff --git a/include/grpc/impl/codegen/log.h b/include/grpc/impl/codegen/log.h
deleted file mode 100644
index e5010c2..0000000
--- a/include/grpc/impl/codegen/log.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef GRPC_IMPL_CODEGEN_LOG_H
-#define GRPC_IMPL_CODEGEN_LOG_H
-
-#include <inttypes.h>
-#include <stdarg.h>
-#include <stdlib.h> /* for abort() */
-
-#include <grpc/impl/codegen/port_platform.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* GPR log API.
-
-   Usage (within grpc):
-
-   int argument1 = 3;
-   char* argument2 = "hello";
-   gpr_log(GPR_DEBUG, "format string %d", argument1);
-   gpr_log(GPR_INFO, "hello world");
-   gpr_log(GPR_ERROR, "%d %s!!", argument1, argument2); */
-
-/* The severity of a log message - use the #defines below when calling into
-   gpr_log to additionally supply file and line data */
-typedef enum gpr_log_severity {
-  GPR_LOG_SEVERITY_DEBUG,
-  GPR_LOG_SEVERITY_INFO,
-  GPR_LOG_SEVERITY_ERROR
-} gpr_log_severity;
-
-#define GPR_LOG_VERBOSITY_UNSET -1
-
-/* Returns a string representation of the log severity */
-const char *gpr_log_severity_string(gpr_log_severity severity);
-
-/* Macros to build log contexts at various severity levels */
-#define GPR_DEBUG __FILE__, __LINE__, GPR_LOG_SEVERITY_DEBUG
-#define GPR_INFO __FILE__, __LINE__, GPR_LOG_SEVERITY_INFO
-#define GPR_ERROR __FILE__, __LINE__, GPR_LOG_SEVERITY_ERROR
-
-/* Log a message. It's advised to use GPR_xxx above to generate the context
- * for each message */
-GPRAPI void gpr_log(const char *file, int line, gpr_log_severity severity,
-                    const char *format, ...) GPRC_PRINT_FORMAT_CHECK(4, 5);
-
-GPRAPI void gpr_log_message(const char *file, int line,
-                            gpr_log_severity severity, const char *message);
-
-/* Set global log verbosity */
-GPRAPI void gpr_set_log_verbosity(gpr_log_severity min_severity_to_print);
-
-GPRAPI void gpr_log_verbosity_init();
-
-/* Log overrides: applications can use this API to intercept logging calls
-   and use their own implementations */
-
-typedef struct {
-  const char *file;
-  int line;
-  gpr_log_severity severity;
-  const char *message;
-} gpr_log_func_args;
-
-typedef void (*gpr_log_func)(gpr_log_func_args *args);
-GPRAPI void gpr_set_log_function(gpr_log_func func);
-
-/* abort() the process if x is zero, having written a line to the log.
-
-   Intended for internal invariants.  If the error can be recovered from,
-   without the possibility of corruption, or might best be reflected via
-   an exception in a higher-level language, consider returning error code.  */
-#define GPR_ASSERT(x)                                 \
-  do {                                                \
-    if (!(x)) {                                       \
-      gpr_log(GPR_ERROR, "assertion failed: %s", #x); \
-      abort();                                        \
-    }                                                 \
-  } while (0)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* GRPC_IMPL_CODEGEN_LOG_H */
diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h
index 7c67bad..4e9be4d 100644
--- a/include/grpc/impl/codegen/port_platform.h
+++ b/include/grpc/impl/codegen/port_platform.h
@@ -155,7 +155,7 @@
 #else /* _LP64 */
 #define GPR_ARCH_32 1
 #endif /* _LP64 */
-#define GPR_CPU_LINUX 1
+#define GPR_CPU_POSIX 1
 #define GPR_GCC_SYNC 1
 #define GPR_GCC_TLS 1
 #define GPR_POSIX_MULTIPOLL_WITH_POLL 1
@@ -167,7 +167,7 @@
 #define GPR_POSIX_ENV 1
 #define GPR_POSIX_FILE 1
 #define GPR_POSIX_TMPFILE 1
-#define GPR_POSIX_LOG
+#define GPR_ANDROID_LOG 1
 #define GPR_POSIX_STRING 1
 #define GPR_POSIX_SUBPROCESS 1
 #define GPR_POSIX_SYNC 1
@@ -234,6 +234,7 @@
 #define GPR_ARCH_32 1
 #endif /* _LP64 */
 #elif defined(__APPLE__)
+#include <Availability.h>
 #include <TargetConditionals.h>
 #ifndef _BSD_SOURCE
 #define _BSD_SOURCE
@@ -246,8 +247,18 @@
 #define GPR_PTHREAD_TLS 1
 #else /* TARGET_OS_IPHONE */
 #define GPR_PLATFORM_STRING "osx"
+#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_7
+#define GPR_CPU_IPHONE 1
+#define GPR_PTHREAD_TLS 1
+#else /* __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_7 */
 #define GPR_CPU_POSIX 1
 #define GPR_GCC_TLS 1
+#endif
+#else /* __MAC_OS_X_VERSION_MIN_REQUIRED */
+#define GPR_CPU_POSIX 1
+#define GPR_GCC_TLS 1
+#endif
 #define GPR_POSIX_CRASH_HANDLER 1
 #endif
 #define GPR_GCC_ATOMIC 1
diff --git a/include/grpc/impl/codegen/slice.h b/include/grpc/impl/codegen/slice.h
index dfc0a77..a2637ee 100644
--- a/include/grpc/impl/codegen/slice.h
+++ b/include/grpc/impl/codegen/slice.h
@@ -34,62 +34,8 @@
 #ifndef GRPC_IMPL_CODEGEN_SLICE_H
 #define GRPC_IMPL_CODEGEN_SLICE_H
 
-#include <grpc/impl/codegen/sync.h>
-
 #include <stddef.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Slice API
-
-   A slice represents a contiguous reference counted array of bytes.
-   It is cheap to take references to a slice, and it is cheap to create a
-   slice pointing to a subset of another slice.
-
-   The data-structure for slices is exposed here to allow non-gpr code to
-   build slices from whatever data they have available.
-
-   When defining interfaces that handle slices, care should be taken to define
-   reference ownership semantics (who should call unref?) and mutability
-   constraints (is the callee allowed to modify the slice?) */
-
-/* Reference count container for gpr_slice. Contains function pointers to
-   increment and decrement reference counts. Implementations should cleanup
-   when the reference count drops to zero.
-   Typically client code should not touch this, and use gpr_slice_malloc,
-   gpr_slice_new, or gpr_slice_new_with_len instead. */
-typedef struct gpr_slice_refcount {
-  void (*ref)(void *);
-  void (*unref)(void *);
-} gpr_slice_refcount;
-
-#define GPR_SLICE_INLINED_SIZE (sizeof(size_t) + sizeof(uint8_t *) - 1)
-
-/* A gpr_slice s, if initialized, represents the byte range
-   s.bytes[0..s.length-1].
-
-   It can have an associated ref count which has a destruction routine to be run
-   when the ref count reaches zero (see gpr_slice_new() and grp_slice_unref()).
-   Multiple gpr_slice values may share a ref count.
-
-   If the slice does not have a refcount, it represents an inlined small piece
-   of data that is copied by value. */
-typedef struct gpr_slice {
-  struct gpr_slice_refcount *refcount;
-  union {
-    struct {
-      uint8_t *bytes;
-      size_t length;
-    } refcounted;
-    struct {
-      uint8_t length;
-      uint8_t bytes[GPR_SLICE_INLINED_SIZE];
-    } inlined;
-  } data;
-} gpr_slice;
-
 #define GPR_SLICE_START_PTR(slice)                  \
   ((slice).refcount ? (slice).data.refcounted.bytes \
                     : (slice).data.inlined.bytes)
@@ -103,88 +49,4 @@
   GPR_SLICE_START_PTR(slice) + GPR_SLICE_LENGTH(slice)
 #define GPR_SLICE_IS_EMPTY(slice) (GPR_SLICE_LENGTH(slice) == 0)
 
-/* Increment the refcount of s. Requires slice is initialized.
-   Returns s. */
-GPRAPI gpr_slice gpr_slice_ref(gpr_slice s);
-
-/* Decrement the ref count of s.  If the ref count of s reaches zero, all
-   slices sharing the ref count are destroyed, and considered no longer
-   initialized.  If s is ultimately derived from a call to gpr_slice_new(start,
-   len, dest) where dest!=NULL , then (*dest)(start) is called, else if s is
-   ultimately derived from a call to gpr_slice_new_with_len(start, len, dest)
-   where dest!=NULL , then (*dest)(start, len).  Requires s initialized.  */
-GPRAPI void gpr_slice_unref(gpr_slice s);
-
-/* Create a slice pointing at some data. Calls malloc to allocate a refcount
-   for the object, and arranges that destroy will be called with the pointer
-   passed in at destruction. */
-GPRAPI gpr_slice gpr_slice_new(void *p, size_t len, void (*destroy)(void *));
-
-/* Equivalent to gpr_slice_new, but with a separate pointer that is
-   passed to the destroy function.  This function can be useful when
-   the data is part of a larger structure that must be destroyed when
-   the data is no longer needed. */
-GPRAPI gpr_slice gpr_slice_new_with_user_data(void *p, size_t len,
-                                              void (*destroy)(void *),
-                                              void *user_data);
-
-/* Equivalent to gpr_slice_new, but with a two argument destroy function that
-   also takes the slice length. */
-GPRAPI gpr_slice gpr_slice_new_with_len(void *p, size_t len,
-                                        void (*destroy)(void *, size_t));
-
-/* Equivalent to gpr_slice_new(malloc(len), len, free), but saves one malloc()
-   call.
-   Aborts if malloc() fails. */
-GPRAPI gpr_slice gpr_slice_malloc(size_t length);
-
-/* Create a slice by copying a string.
-   Does not preserve null terminators.
-   Equivalent to:
-     size_t len = strlen(source);
-     gpr_slice slice = gpr_slice_malloc(len);
-     memcpy(slice->data, source, len); */
-GPRAPI gpr_slice gpr_slice_from_copied_string(const char *source);
-
-/* Create a slice by copying a buffer.
-   Equivalent to:
-     gpr_slice slice = gpr_slice_malloc(len);
-     memcpy(slice->data, source, len); */
-GPRAPI gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t len);
-
-/* Create a slice pointing to constant memory */
-GPRAPI gpr_slice gpr_slice_from_static_string(const char *source);
-
-/* Return a result slice derived from s, which shares a ref count with s, where
-   result.data==s.data+begin, and result.length==end-begin.
-   The ref count of s is increased by one.
-   Requires s initialized, begin <= end, begin <= s.length, and
-   end <= source->length. */
-GPRAPI gpr_slice gpr_slice_sub(gpr_slice s, size_t begin, size_t end);
-
-/* The same as gpr_slice_sub, but without altering the ref count */
-GPRAPI gpr_slice gpr_slice_sub_no_ref(gpr_slice s, size_t begin, size_t end);
-
-/* Splits s into two: modifies s to be s[0:split], and returns a new slice,
-   sharing a refcount with s, that contains s[split:s.length].
-   Requires s intialized, split <= s.length */
-GPRAPI gpr_slice gpr_slice_split_tail(gpr_slice *s, size_t split);
-
-/* Splits s into two: modifies s to be s[split:s.length], and returns a new
-   slice, sharing a refcount with s, that contains s[0:split].
-   Requires s intialized, split <= s.length */
-GPRAPI gpr_slice gpr_slice_split_head(gpr_slice *s, size_t split);
-
-GPRAPI gpr_slice gpr_empty_slice(void);
-
-/* Returns <0 if a < b, ==0 if a == b, >0 if a > b
-   The order is arbitrary, and is not guaranteed to be stable across different
-   versions of the API. */
-GPRAPI int gpr_slice_cmp(gpr_slice a, gpr_slice b);
-GPRAPI int gpr_slice_str_cmp(gpr_slice a, const char *b);
-
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* GRPC_IMPL_CODEGEN_SLICE_H */
diff --git a/include/grpc/impl/codegen/slice_buffer.h b/include/grpc/impl/codegen/slice_buffer.h
deleted file mode 100644
index 7858021..0000000
--- a/include/grpc/impl/codegen/slice_buffer.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef GRPC_IMPL_CODEGEN_SLICE_BUFFER_H
-#define GRPC_IMPL_CODEGEN_SLICE_BUFFER_H
-
-#include <grpc/impl/codegen/slice.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define GRPC_SLICE_BUFFER_INLINE_ELEMENTS 8
-
-/* Represents an expandable array of slices, to be interpreted as a
-   single item. */
-typedef struct {
-  /* slices in the array */
-  gpr_slice *slices;
-  /* the number of slices in the array */
-  size_t count;
-  /* the number of slices allocated in the array */
-  size_t capacity;
-  /* the combined length of all slices in the array */
-  size_t length;
-  /* inlined elements to avoid allocations */
-  gpr_slice inlined[GRPC_SLICE_BUFFER_INLINE_ELEMENTS];
-} gpr_slice_buffer;
-
-/* initialize a slice buffer */
-GPRAPI void gpr_slice_buffer_init(gpr_slice_buffer *sb);
-/* destroy a slice buffer - unrefs any held elements */
-GPRAPI void gpr_slice_buffer_destroy(gpr_slice_buffer *sb);
-/* Add an element to a slice buffer - takes ownership of the slice.
-   This function is allowed to concatenate the passed in slice to the end of
-   some other slice if desired by the slice buffer. */
-GPRAPI void gpr_slice_buffer_add(gpr_slice_buffer *sb, gpr_slice slice);
-/* add an element to a slice buffer - takes ownership of the slice and returns
-   the index of the slice.
-   Guarantees that the slice will not be concatenated at the end of another
-   slice (i.e. the data for this slice will begin at the first byte of the
-   slice at the returned index in sb->slices)
-   The implementation MAY decide to concatenate data at the end of a small
-   slice added in this fashion. */
-GPRAPI size_t gpr_slice_buffer_add_indexed(gpr_slice_buffer *sb,
-                                           gpr_slice slice);
-GPRAPI void gpr_slice_buffer_addn(gpr_slice_buffer *sb, gpr_slice *slices,
-                                  size_t n);
-/* add a very small (less than 8 bytes) amount of data to the end of a slice
-   buffer: returns a pointer into which to add the data */
-GPRAPI uint8_t *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, size_t len);
-/* pop the last buffer, but don't unref it */
-GPRAPI void gpr_slice_buffer_pop(gpr_slice_buffer *sb);
-/* clear a slice buffer, unref all elements */
-GPRAPI void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb);
-/* swap the contents of two slice buffers */
-GPRAPI void gpr_slice_buffer_swap(gpr_slice_buffer *a, gpr_slice_buffer *b);
-/* move all of the elements of src into dst */
-GPRAPI void gpr_slice_buffer_move_into(gpr_slice_buffer *src,
-                                       gpr_slice_buffer *dst);
-/* remove n bytes from the end of a slice buffer */
-GPRAPI void gpr_slice_buffer_trim_end(gpr_slice_buffer *src, size_t n,
-                                      gpr_slice_buffer *garbage);
-/* move the first n bytes of src into dst */
-GPRAPI void gpr_slice_buffer_move_first(gpr_slice_buffer *src, size_t n,
-                                        gpr_slice_buffer *dst);
-/* take the first slice in the slice buffer */
-GPRAPI gpr_slice gpr_slice_buffer_take_first(gpr_slice_buffer *src);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* GRPC_IMPL_CODEGEN_SLICE_BUFFER_H */
diff --git a/include/grpc/impl/codegen/sync.h b/include/grpc/impl/codegen/sync.h
index 5542482..96aec0c 100644
--- a/include/grpc/impl/codegen/sync.h
+++ b/include/grpc/impl/codegen/sync.h
@@ -64,257 +64,4 @@
 #error Unable to determine platform for sync
 #endif
 
-#include <grpc/impl/codegen/time.h> /* for gpr_timespec */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* --- Mutex interface ---
-
-   At most one thread may hold an exclusive lock on a mutex at any given time.
-   Actions taken by a thread that holds a mutex exclusively happen after
-   actions taken by all previous holders of the mutex.  Variables of type
-   gpr_mu are uninitialized when first declared.  */
-
-/* Initialize *mu.  Requires:  *mu uninitialized.  */
-GPRAPI void gpr_mu_init(gpr_mu *mu);
-
-/* Cause *mu no longer to be initialized, freeing any memory in use.  Requires:
-   *mu initialized; no other concurrent operation on *mu.  */
-GPRAPI void gpr_mu_destroy(gpr_mu *mu);
-
-/* Wait until no thread has a lock on *mu, cause the calling thread to own an
-   exclusive lock on *mu, then return.  May block indefinitely or crash if the
-   calling thread has a lock on *mu.  Requires:  *mu initialized.  */
-GPRAPI void gpr_mu_lock(gpr_mu *mu);
-
-/* Release an exclusive lock on *mu held by the calling thread.  Requires:  *mu
-   initialized; the calling thread holds an exclusive lock on *mu.  */
-GPRAPI void gpr_mu_unlock(gpr_mu *mu);
-
-/* Without blocking, attempt to acquire an exclusive lock on *mu for the
-   calling thread, then return non-zero iff success.  Fail, if any thread holds
-   the lock; succeeds with high probability if no thread holds the lock.
-   Requires:  *mu initialized.  */
-GPRAPI int gpr_mu_trylock(gpr_mu *mu);
-
-/* --- Condition variable interface ---
-
-   A while-loop should be used with gpr_cv_wait() when waiting for conditions
-   to become true.  See the example below.  Variables of type gpr_cv are
-   uninitialized when first declared.  */
-
-/* Initialize *cv.  Requires:  *cv uninitialized.  */
-GPRAPI void gpr_cv_init(gpr_cv *cv);
-
-/* Cause *cv no longer to be initialized, freeing any memory in use.  Requires:
-   *cv initialized; no other concurrent operation on *cv.*/
-GPRAPI void gpr_cv_destroy(gpr_cv *cv);
-
-/* Atomically release *mu and wait on *cv.  When the calling thread is woken
-   from *cv or the deadline abs_deadline is exceeded, execute gpr_mu_lock(mu)
-   and return whether the deadline was exceeded.  Use
-   abs_deadline==gpr_inf_future for no deadline.  abs_deadline can be either
-   an absolute deadline, or a GPR_TIMESPAN.  May return even when not
-   woken explicitly.  Requires:  *mu and *cv initialized; the calling thread
-   holds an exclusive lock on *mu.  */
-GPRAPI int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline);
-
-/* If any threads are waiting on *cv, wake at least one.
-   Clients may treat this as an optimization of gpr_cv_broadcast()
-   for use in the case where waking more than one waiter is not useful.
-   Requires:  *cv initialized.  */
-GPRAPI void gpr_cv_signal(gpr_cv *cv);
-
-/* Wake all threads waiting on *cv.  Requires:  *cv initialized.  */
-GPRAPI void gpr_cv_broadcast(gpr_cv *cv);
-
-/* --- One-time initialization ---
-
-   gpr_once must be declared with static storage class, and initialized with
-   GPR_ONCE_INIT.  e.g.,
-     static gpr_once once_var = GPR_ONCE_INIT;     */
-
-/* Ensure that (*init_routine)() has been called exactly once (for the
-   specified gpr_once instance) and then return.
-   If multiple threads call gpr_once() on the same gpr_once instance, one of
-   them will call (*init_routine)(), and the others will block until that call
-   finishes.*/
-GPRAPI void gpr_once_init(gpr_once *once, void (*init_routine)(void));
-
-/* --- One-time event notification ---
-
-  These operations act on a gpr_event, which should be initialized with
-  gpr_ev_init(), or with GPR_EVENT_INIT if static, e.g.,
-       static gpr_event event_var = GPR_EVENT_INIT;
-  It requires no destruction.  */
-
-/* Initialize *ev. */
-GPRAPI void gpr_event_init(gpr_event *ev);
-
-/* Set *ev so that gpr_event_get() and gpr_event_wait() will return value.
-   Requires:  *ev initialized; value != NULL; no prior or concurrent calls to
-   gpr_event_set(ev, ...) since initialization.  */
-GPRAPI void gpr_event_set(gpr_event *ev, void *value);
-
-/* Return the value set by gpr_event_set(ev, ...), or NULL if no such call has
-   completed.  If the result is non-NULL, all operations that occurred prior to
-   the gpr_event_set(ev, ...) set will be visible after this call returns.
-   Requires:  *ev initialized.  This operation is faster than acquiring a mutex
-   on most platforms.  */
-GPRAPI void *gpr_event_get(gpr_event *ev);
-
-/* Wait until *ev is set by gpr_event_set(ev, ...), or abs_deadline is
-   exceeded, then return gpr_event_get(ev).  Requires:  *ev initialized.  Use
-   abs_deadline==gpr_inf_future for no deadline.  When the event has been
-   signalled before the call, this operation is faster than acquiring a mutex
-   on most platforms.  */
-GPRAPI void *gpr_event_wait(gpr_event *ev, gpr_timespec abs_deadline);
-
-/* --- Reference counting ---
-
-   These calls act on the type gpr_refcount.  It requires no destruction.  */
-
-/* Initialize *r to value n.  */
-GPRAPI void gpr_ref_init(gpr_refcount *r, int n);
-
-/* Increment the reference count *r.  Requires *r initialized. */
-GPRAPI void gpr_ref(gpr_refcount *r);
-
-/* Increment the reference count *r.  Requires *r initialized.
-   Crashes if refcount is zero */
-GPRAPI void gpr_ref_non_zero(gpr_refcount *r);
-
-/* Increment the reference count *r by n.  Requires *r initialized, n > 0. */
-GPRAPI void gpr_refn(gpr_refcount *r, int n);
-
-/* Decrement the reference count *r and return non-zero iff it has reached
-   zero. .  Requires *r initialized. */
-GPRAPI int gpr_unref(gpr_refcount *r);
-
-/* --- Stats counters ---
-
-   These calls act on the integral type gpr_stats_counter.  It requires no
-   destruction.  Static instances may be initialized with
-       gpr_stats_counter c = GPR_STATS_INIT;
-   Beware:  These operations do not imply memory barriers.  Do not use them to
-   synchronize other events.  */
-
-/* Initialize *c to the value n. */
-GPRAPI void gpr_stats_init(gpr_stats_counter *c, intptr_t n);
-
-/* *c += inc.  Requires: *c initialized. */
-GPRAPI void gpr_stats_inc(gpr_stats_counter *c, intptr_t inc);
-
-/* Return *c.  Requires: *c initialized. */
-GPRAPI intptr_t gpr_stats_read(const gpr_stats_counter *c);
-
-/* ==================Example use of interface===================
-   A producer-consumer queue of up to N integers,
-   illustrating the use of the calls in this interface. */
-#if 0
-
-#define N 4
-
-   typedef struct queue {
-     gpr_cv non_empty;  /* Signalled when length becomes non-zero. */
-     gpr_cv non_full;   /* Signalled when length becomes non-N. */
-     gpr_mu mu;         /* Protects all fields below.
-                            (That is, except during initialization or
-                            destruction, the fields below should be accessed
-                            only by a thread that holds mu.) */
-     int head;           /* Index of head of queue 0..N-1. */
-     int length;         /* Number of valid elements in queue 0..N. */
-     int elem[N];        /* elem[head .. head+length-1] are queue elements. */
-   } queue;
-
-   /* Initialize *q. */
-   void queue_init(queue *q) {
-     gpr_mu_init(&q->mu);
-     gpr_cv_init(&q->non_empty);
-     gpr_cv_init(&q->non_full);
-     q->head = 0;
-     q->length = 0;
-   }
-
-   /* Free storage associated with *q. */
-   void queue_destroy(queue *q) {
-     gpr_mu_destroy(&q->mu);
-     gpr_cv_destroy(&q->non_empty);
-     gpr_cv_destroy(&q->non_full);
-   }
-
-   /* Wait until there is room in *q, then append x to *q. */
-   void queue_append(queue *q, int x) {
-     gpr_mu_lock(&q->mu);
-     /* To wait for a predicate without a deadline, loop on the negation of the
-        predicate, and use gpr_cv_wait(..., gpr_inf_future) inside the loop
-        to release the lock, wait, and reacquire on each iteration.  Code that
-        makes the condition true should use gpr_cv_broadcast() on the
-        corresponding condition variable.  The predicate must be on state
-        protected by the lock.  */
-     while (q->length == N) {
-       gpr_cv_wait(&q->non_full, &q->mu, gpr_inf_future);
-     }
-     if (q->length == 0) {  /* Wake threads blocked in queue_remove(). */
-       /* It's normal to use gpr_cv_broadcast() or gpr_signal() while
-          holding the lock. */
-       gpr_cv_broadcast(&q->non_empty);
-     }
-     q->elem[(q->head + q->length) % N] = x;
-     q->length++;
-     gpr_mu_unlock(&q->mu);
-   }
-
-   /* If it can be done without blocking, append x to *q and return non-zero.
-      Otherwise return 0. */
-   int queue_try_append(queue *q, int x) {
-     int result = 0;
-     if (gpr_mu_trylock(&q->mu)) {
-       if (q->length != N) {
-         if (q->length == 0) {  /* Wake threads blocked in queue_remove(). */
-           gpr_cv_broadcast(&q->non_empty);
-         }
-         q->elem[(q->head + q->length) % N] = x;
-         q->length++;
-         result = 1;
-       }
-       gpr_mu_unlock(&q->mu);
-     }
-     return result;
-   }
-
-   /* Wait until the *q is non-empty or deadline abs_deadline passes.  If the
-      queue is non-empty, remove its head entry, place it in *head, and return
-      non-zero.  Otherwise return 0.  */
-   int queue_remove(queue *q, int *head, gpr_timespec abs_deadline) {
-     int result = 0;
-     gpr_mu_lock(&q->mu);
-     /* To wait for a predicate with a deadline, loop on the negation of the
-        predicate or until gpr_cv_wait() returns true.  Code that makes
-        the condition true should use gpr_cv_broadcast() on the corresponding
-        condition variable.  The predicate must be on state protected by the
-        lock. */
-     while (q->length == 0 &&
-            !gpr_cv_wait(&q->non_empty, &q->mu, abs_deadline)) {
-     }
-     if (q->length != 0) {    /* Queue is non-empty. */
-       result = 1;
-       if (q->length == N) {  /* Wake threads blocked in queue_append(). */
-         gpr_cv_broadcast(&q->non_full);
-       }
-       *head = q->elem[q->head];
-       q->head = (q->head + 1) % N;
-       q->length--;
-     } /* else deadline exceeded */
-     gpr_mu_unlock(&q->mu);
-     return result;
-   }
-#endif /* 0 */
-
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* GRPC_IMPL_CODEGEN_SYNC_H */
diff --git a/include/grpc/impl/codegen/time.h b/include/grpc/impl/codegen/time.h
deleted file mode 100644
index 381e267..0000000
--- a/include/grpc/impl/codegen/time.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef GRPC_IMPL_CODEGEN_TIME_H
-#define GRPC_IMPL_CODEGEN_TIME_H
-/* Time support.
-   We use gpr_timespec, which is analogous to struct timespec.  On some
-   machines, absolute times may be in local time.  */
-
-#include <grpc/impl/codegen/port_platform.h>
-#include <stddef.h>
-#include <time.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* The clocks we support. */
-typedef enum {
-  /* Monotonic clock. Epoch undefined. Always moves forwards. */
-  GPR_CLOCK_MONOTONIC = 0,
-  /* Realtime clock. May jump forwards or backwards. Settable by
-     the system administrator. Has its epoch at 0:00:00 UTC 1 Jan 1970. */
-  GPR_CLOCK_REALTIME,
-  /* CPU cycle time obtained by rdtsc instruction on x86 platforms. Epoch
-     undefined. Degrades to GPR_CLOCK_REALTIME on other platforms. */
-  GPR_CLOCK_PRECISE,
-  /* Unmeasurable clock type: no base, created by taking the difference
-     between two times */
-  GPR_TIMESPAN
-} gpr_clock_type;
-
-typedef struct gpr_timespec {
-  int64_t tv_sec;
-  int32_t tv_nsec;
-  /** Against which clock was this time measured? (or GPR_TIMESPAN if
-      this is a relative time meaure) */
-  gpr_clock_type clock_type;
-} gpr_timespec;
-
-/* Time constants. */
-GPRAPI gpr_timespec
-gpr_time_0(gpr_clock_type type); /* The zero time interval. */
-GPRAPI gpr_timespec gpr_inf_future(gpr_clock_type type); /* The far future */
-GPRAPI gpr_timespec gpr_inf_past(gpr_clock_type type);   /* The far past. */
-
-#define GPR_MS_PER_SEC 1000
-#define GPR_US_PER_SEC 1000000
-#define GPR_NS_PER_SEC 1000000000
-#define GPR_NS_PER_MS 1000000
-#define GPR_NS_PER_US 1000
-#define GPR_US_PER_MS 1000
-
-/* initialize time subsystem */
-GPRAPI void gpr_time_init(void);
-
-/* Return the current time measured from the given clocks epoch. */
-GPRAPI gpr_timespec gpr_now(gpr_clock_type clock);
-
-/* Convert a timespec from one clock to another */
-GPRAPI gpr_timespec gpr_convert_clock_type(gpr_timespec t,
-                                           gpr_clock_type target_clock);
-
-/* Return -ve, 0, or +ve according to whether a < b, a == b, or a > b
-   respectively.  */
-GPRAPI int gpr_time_cmp(gpr_timespec a, gpr_timespec b);
-
-GPRAPI gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b);
-GPRAPI gpr_timespec gpr_time_min(gpr_timespec a, gpr_timespec b);
-
-/* Add and subtract times.  Calculations saturate at infinities. */
-GPRAPI gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b);
-GPRAPI gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b);
-
-/* Return a timespec representing a given number of time units. INT64_MIN is
-   interpreted as gpr_inf_past, and INT64_MAX as gpr_inf_future.  */
-GPRAPI gpr_timespec gpr_time_from_micros(int64_t x, gpr_clock_type clock_type);
-GPRAPI gpr_timespec gpr_time_from_nanos(int64_t x, gpr_clock_type clock_type);
-GPRAPI gpr_timespec gpr_time_from_millis(int64_t x, gpr_clock_type clock_type);
-GPRAPI gpr_timespec gpr_time_from_seconds(int64_t x, gpr_clock_type clock_type);
-GPRAPI gpr_timespec gpr_time_from_minutes(int64_t x, gpr_clock_type clock_type);
-GPRAPI gpr_timespec gpr_time_from_hours(int64_t x, gpr_clock_type clock_type);
-
-GPRAPI int32_t gpr_time_to_millis(gpr_timespec timespec);
-
-/* Return 1 if two times are equal or within threshold of each other,
-   0 otherwise */
-GPRAPI int gpr_time_similar(gpr_timespec a, gpr_timespec b,
-                            gpr_timespec threshold);
-
-/* Sleep until at least 'until' - an absolute timeout */
-GPRAPI void gpr_sleep_until(gpr_timespec until);
-
-GPRAPI double gpr_timespec_to_micros(gpr_timespec t);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* GRPC_IMPL_CODEGEN_TIME_H */
diff --git a/include/grpc/module.modulemap b/include/grpc/module.modulemap
index 02fb2f3..51bfef2 100644
--- a/include/grpc/module.modulemap
+++ b/include/grpc/module.modulemap
@@ -4,8 +4,6 @@
   header "byte_buffer_reader.h"
   header "grpc_security.h"
   header "grpc_security_constants.h"
-  header "impl/codegen/alloc.h"
-  header "impl/codegen/byte_buffer_reader.h"
   header "support/alloc.h"
   header "support/port_platform.h"
   header "support/string_util.h"
diff --git a/include/grpc/support/alloc.h b/include/grpc/support/alloc.h
index 8d03af8..327a9ff 100644
--- a/include/grpc/support/alloc.h
+++ b/include/grpc/support/alloc.h
@@ -34,6 +34,41 @@
 #ifndef GRPC_SUPPORT_ALLOC_H
 #define GRPC_SUPPORT_ALLOC_H
 
-#include <grpc/impl/codegen/alloc.h>
+#include <stddef.h>
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct gpr_allocation_functions {
+  void *(*malloc_fn)(size_t size);
+  void *(*realloc_fn)(void *ptr, size_t size);
+  void (*free_fn)(void *ptr);
+} gpr_allocation_functions;
+
+/* malloc, never returns NULL */
+GPRAPI void *gpr_malloc(size_t size);
+/* free */
+GPRAPI void gpr_free(void *ptr);
+/* realloc, never returns NULL */
+GPRAPI void *gpr_realloc(void *p, size_t size);
+/* aligned malloc, never returns NULL, will align to 1 << alignment_log */
+GPRAPI void *gpr_malloc_aligned(size_t size, size_t alignment_log);
+/* free memory allocated by gpr_malloc_aligned */
+GPRAPI void gpr_free_aligned(void *ptr);
+
+/** Request the family of allocation functions in \a functions be used. NOTE
+ * that this request will be honored in a *best effort* basis and that no
+ * guarantees are made about the default functions (eg, malloc) being called. */
+GPRAPI void gpr_set_allocation_functions(gpr_allocation_functions functions);
+
+/** Return the family of allocation functions currently in effect. */
+GPRAPI gpr_allocation_functions gpr_get_allocation_functions();
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* GRPC_SUPPORT_ALLOC_H */
diff --git a/include/grpc/support/log.h b/include/grpc/support/log.h
index 3fdba04..5acf17f 100644
--- a/include/grpc/support/log.h
+++ b/include/grpc/support/log.h
@@ -34,6 +34,85 @@
 #ifndef GRPC_SUPPORT_LOG_H
 #define GRPC_SUPPORT_LOG_H
 
-#include <grpc/impl/codegen/log.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdlib.h> /* for abort() */
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* GPR log API.
+
+   Usage (within grpc):
+
+   int argument1 = 3;
+   char* argument2 = "hello";
+   gpr_log(GPR_DEBUG, "format string %d", argument1);
+   gpr_log(GPR_INFO, "hello world");
+   gpr_log(GPR_ERROR, "%d %s!!", argument1, argument2); */
+
+/* The severity of a log message - use the #defines below when calling into
+   gpr_log to additionally supply file and line data */
+typedef enum gpr_log_severity {
+  GPR_LOG_SEVERITY_DEBUG,
+  GPR_LOG_SEVERITY_INFO,
+  GPR_LOG_SEVERITY_ERROR
+} gpr_log_severity;
+
+#define GPR_LOG_VERBOSITY_UNSET -1
+
+/* Returns a string representation of the log severity */
+const char *gpr_log_severity_string(gpr_log_severity severity);
+
+/* Macros to build log contexts at various severity levels */
+#define GPR_DEBUG __FILE__, __LINE__, GPR_LOG_SEVERITY_DEBUG
+#define GPR_INFO __FILE__, __LINE__, GPR_LOG_SEVERITY_INFO
+#define GPR_ERROR __FILE__, __LINE__, GPR_LOG_SEVERITY_ERROR
+
+/* Log a message. It's advised to use GPR_xxx above to generate the context
+ * for each message */
+GPRAPI void gpr_log(const char *file, int line, gpr_log_severity severity,
+                    const char *format, ...) GPRC_PRINT_FORMAT_CHECK(4, 5);
+
+GPRAPI void gpr_log_message(const char *file, int line,
+                            gpr_log_severity severity, const char *message);
+
+/* Set global log verbosity */
+GPRAPI void gpr_set_log_verbosity(gpr_log_severity min_severity_to_print);
+
+GPRAPI void gpr_log_verbosity_init();
+
+/* Log overrides: applications can use this API to intercept logging calls
+   and use their own implementations */
+
+typedef struct {
+  const char *file;
+  int line;
+  gpr_log_severity severity;
+  const char *message;
+} gpr_log_func_args;
+
+typedef void (*gpr_log_func)(gpr_log_func_args *args);
+GPRAPI void gpr_set_log_function(gpr_log_func func);
+
+/* abort() the process if x is zero, having written a line to the log.
+
+   Intended for internal invariants.  If the error can be recovered from,
+   without the possibility of corruption, or might best be reflected via
+   an exception in a higher-level language, consider returning error code.  */
+#define GPR_ASSERT(x)                                 \
+  do {                                                \
+    if (!(x)) {                                       \
+      gpr_log(GPR_ERROR, "assertion failed: %s", #x); \
+      abort();                                        \
+    }                                                 \
+  } while (0)
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* GRPC_SUPPORT_LOG_H */
diff --git a/include/grpc/support/slice.h b/include/grpc/support/slice.h
index b45db03..b31fe6c 100644
--- a/include/grpc/support/slice.h
+++ b/include/grpc/support/slice.h
@@ -35,5 +35,94 @@
 #define GRPC_SUPPORT_SLICE_H
 
 #include <grpc/impl/codegen/slice.h>
+#include <grpc/support/sync.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Increment the refcount of s. Requires slice is initialized.
+   Returns s. */
+GPRAPI gpr_slice gpr_slice_ref(gpr_slice s);
+
+/* Decrement the ref count of s.  If the ref count of s reaches zero, all
+   slices sharing the ref count are destroyed, and considered no longer
+   initialized.  If s is ultimately derived from a call to gpr_slice_new(start,
+   len, dest) where dest!=NULL , then (*dest)(start) is called, else if s is
+   ultimately derived from a call to gpr_slice_new_with_len(start, len, dest)
+   where dest!=NULL , then (*dest)(start, len).  Requires s initialized.  */
+GPRAPI void gpr_slice_unref(gpr_slice s);
+
+/* Create a slice pointing at some data. Calls malloc to allocate a refcount
+   for the object, and arranges that destroy will be called with the pointer
+   passed in at destruction. */
+GPRAPI gpr_slice gpr_slice_new(void *p, size_t len, void (*destroy)(void *));
+
+/* Equivalent to gpr_slice_new, but with a separate pointer that is
+   passed to the destroy function.  This function can be useful when
+   the data is part of a larger structure that must be destroyed when
+   the data is no longer needed. */
+GPRAPI gpr_slice gpr_slice_new_with_user_data(void *p, size_t len,
+                                              void (*destroy)(void *),
+                                              void *user_data);
+
+/* Equivalent to gpr_slice_new, but with a two argument destroy function that
+   also takes the slice length. */
+GPRAPI gpr_slice gpr_slice_new_with_len(void *p, size_t len,
+                                        void (*destroy)(void *, size_t));
+
+/* Equivalent to gpr_slice_new(malloc(len), len, free), but saves one malloc()
+   call.
+   Aborts if malloc() fails. */
+GPRAPI gpr_slice gpr_slice_malloc(size_t length);
+
+/* Create a slice by copying a string.
+   Does not preserve null terminators.
+   Equivalent to:
+     size_t len = strlen(source);
+     gpr_slice slice = gpr_slice_malloc(len);
+     memcpy(slice->data, source, len); */
+GPRAPI gpr_slice gpr_slice_from_copied_string(const char *source);
+
+/* Create a slice by copying a buffer.
+   Equivalent to:
+     gpr_slice slice = gpr_slice_malloc(len);
+     memcpy(slice->data, source, len); */
+GPRAPI gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t len);
+
+/* Create a slice pointing to constant memory */
+GPRAPI gpr_slice gpr_slice_from_static_string(const char *source);
+
+/* Return a result slice derived from s, which shares a ref count with s, where
+   result.data==s.data+begin, and result.length==end-begin.
+   The ref count of s is increased by one.
+   Requires s initialized, begin <= end, begin <= s.length, and
+   end <= source->length. */
+GPRAPI gpr_slice gpr_slice_sub(gpr_slice s, size_t begin, size_t end);
+
+/* The same as gpr_slice_sub, but without altering the ref count */
+GPRAPI gpr_slice gpr_slice_sub_no_ref(gpr_slice s, size_t begin, size_t end);
+
+/* Splits s into two: modifies s to be s[0:split], and returns a new slice,
+   sharing a refcount with s, that contains s[split:s.length].
+   Requires s intialized, split <= s.length */
+GPRAPI gpr_slice gpr_slice_split_tail(gpr_slice *s, size_t split);
+
+/* Splits s into two: modifies s to be s[split:s.length], and returns a new
+   slice, sharing a refcount with s, that contains s[0:split].
+   Requires s intialized, split <= s.length */
+GPRAPI gpr_slice gpr_slice_split_head(gpr_slice *s, size_t split);
+
+GPRAPI gpr_slice gpr_empty_slice(void);
+
+/* Returns <0 if a < b, ==0 if a == b, >0 if a > b
+   The order is arbitrary, and is not guaranteed to be stable across different
+   versions of the API. */
+GPRAPI int gpr_slice_cmp(gpr_slice a, gpr_slice b);
+GPRAPI int gpr_slice_str_cmp(gpr_slice a, const char *b);
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* GRPC_SUPPORT_SLICE_H */
diff --git a/include/grpc/support/slice_buffer.h b/include/grpc/support/slice_buffer.h
index cf7a89f..3920432 100644
--- a/include/grpc/support/slice_buffer.h
+++ b/include/grpc/support/slice_buffer.h
@@ -34,6 +34,54 @@
 #ifndef GRPC_SUPPORT_SLICE_BUFFER_H
 #define GRPC_SUPPORT_SLICE_BUFFER_H
 
-#include <grpc/impl/codegen/slice_buffer.h>
+#include <grpc/support/slice.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* initialize a slice buffer */
+GPRAPI void gpr_slice_buffer_init(gpr_slice_buffer *sb);
+/* destroy a slice buffer - unrefs any held elements */
+GPRAPI void gpr_slice_buffer_destroy(gpr_slice_buffer *sb);
+/* Add an element to a slice buffer - takes ownership of the slice.
+   This function is allowed to concatenate the passed in slice to the end of
+   some other slice if desired by the slice buffer. */
+GPRAPI void gpr_slice_buffer_add(gpr_slice_buffer *sb, gpr_slice slice);
+/* add an element to a slice buffer - takes ownership of the slice and returns
+   the index of the slice.
+   Guarantees that the slice will not be concatenated at the end of another
+   slice (i.e. the data for this slice will begin at the first byte of the
+   slice at the returned index in sb->slices)
+   The implementation MAY decide to concatenate data at the end of a small
+   slice added in this fashion. */
+GPRAPI size_t gpr_slice_buffer_add_indexed(gpr_slice_buffer *sb,
+                                           gpr_slice slice);
+GPRAPI void gpr_slice_buffer_addn(gpr_slice_buffer *sb, gpr_slice *slices,
+                                  size_t n);
+/* add a very small (less than 8 bytes) amount of data to the end of a slice
+   buffer: returns a pointer into which to add the data */
+GPRAPI uint8_t *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, size_t len);
+/* pop the last buffer, but don't unref it */
+GPRAPI void gpr_slice_buffer_pop(gpr_slice_buffer *sb);
+/* clear a slice buffer, unref all elements */
+GPRAPI void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb);
+/* swap the contents of two slice buffers */
+GPRAPI void gpr_slice_buffer_swap(gpr_slice_buffer *a, gpr_slice_buffer *b);
+/* move all of the elements of src into dst */
+GPRAPI void gpr_slice_buffer_move_into(gpr_slice_buffer *src,
+                                       gpr_slice_buffer *dst);
+/* remove n bytes from the end of a slice buffer */
+GPRAPI void gpr_slice_buffer_trim_end(gpr_slice_buffer *src, size_t n,
+                                      gpr_slice_buffer *garbage);
+/* move the first n bytes of src into dst */
+GPRAPI void gpr_slice_buffer_move_first(gpr_slice_buffer *src, size_t n,
+                                        gpr_slice_buffer *dst);
+/* take the first slice in the slice buffer */
+GPRAPI gpr_slice gpr_slice_buffer_take_first(gpr_slice_buffer *src);
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* GRPC_SUPPORT_SLICE_BUFFER_H */
diff --git a/include/grpc/support/sync.h b/include/grpc/support/sync.h
index a014580..a7bbb38 100644
--- a/include/grpc/support/sync.h
+++ b/include/grpc/support/sync.h
@@ -34,6 +34,258 @@
 #ifndef GRPC_SUPPORT_SYNC_H
 #define GRPC_SUPPORT_SYNC_H
 
+#include <grpc/impl/codegen/gpr_types.h> /* for gpr_timespec */
 #include <grpc/impl/codegen/sync.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* --- Mutex interface ---
+
+   At most one thread may hold an exclusive lock on a mutex at any given time.
+   Actions taken by a thread that holds a mutex exclusively happen after
+   actions taken by all previous holders of the mutex.  Variables of type
+   gpr_mu are uninitialized when first declared.  */
+
+/* Initialize *mu.  Requires:  *mu uninitialized.  */
+GPRAPI void gpr_mu_init(gpr_mu *mu);
+
+/* Cause *mu no longer to be initialized, freeing any memory in use.  Requires:
+   *mu initialized; no other concurrent operation on *mu.  */
+GPRAPI void gpr_mu_destroy(gpr_mu *mu);
+
+/* Wait until no thread has a lock on *mu, cause the calling thread to own an
+   exclusive lock on *mu, then return.  May block indefinitely or crash if the
+   calling thread has a lock on *mu.  Requires:  *mu initialized.  */
+GPRAPI void gpr_mu_lock(gpr_mu *mu);
+
+/* Release an exclusive lock on *mu held by the calling thread.  Requires:  *mu
+   initialized; the calling thread holds an exclusive lock on *mu.  */
+GPRAPI void gpr_mu_unlock(gpr_mu *mu);
+
+/* Without blocking, attempt to acquire an exclusive lock on *mu for the
+   calling thread, then return non-zero iff success.  Fail, if any thread holds
+   the lock; succeeds with high probability if no thread holds the lock.
+   Requires:  *mu initialized.  */
+GPRAPI int gpr_mu_trylock(gpr_mu *mu);
+
+/* --- Condition variable interface ---
+
+   A while-loop should be used with gpr_cv_wait() when waiting for conditions
+   to become true.  See the example below.  Variables of type gpr_cv are
+   uninitialized when first declared.  */
+
+/* Initialize *cv.  Requires:  *cv uninitialized.  */
+GPRAPI void gpr_cv_init(gpr_cv *cv);
+
+/* Cause *cv no longer to be initialized, freeing any memory in use.  Requires:
+   *cv initialized; no other concurrent operation on *cv.*/
+GPRAPI void gpr_cv_destroy(gpr_cv *cv);
+
+/* Atomically release *mu and wait on *cv.  When the calling thread is woken
+   from *cv or the deadline abs_deadline is exceeded, execute gpr_mu_lock(mu)
+   and return whether the deadline was exceeded.  Use
+   abs_deadline==gpr_inf_future for no deadline.  abs_deadline can be either
+   an absolute deadline, or a GPR_TIMESPAN.  May return even when not
+   woken explicitly.  Requires:  *mu and *cv initialized; the calling thread
+   holds an exclusive lock on *mu.  */
+GPRAPI int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline);
+
+/* If any threads are waiting on *cv, wake at least one.
+   Clients may treat this as an optimization of gpr_cv_broadcast()
+   for use in the case where waking more than one waiter is not useful.
+   Requires:  *cv initialized.  */
+GPRAPI void gpr_cv_signal(gpr_cv *cv);
+
+/* Wake all threads waiting on *cv.  Requires:  *cv initialized.  */
+GPRAPI void gpr_cv_broadcast(gpr_cv *cv);
+
+/* --- One-time initialization ---
+
+   gpr_once must be declared with static storage class, and initialized with
+   GPR_ONCE_INIT.  e.g.,
+     static gpr_once once_var = GPR_ONCE_INIT;     */
+
+/* Ensure that (*init_routine)() has been called exactly once (for the
+   specified gpr_once instance) and then return.
+   If multiple threads call gpr_once() on the same gpr_once instance, one of
+   them will call (*init_routine)(), and the others will block until that call
+   finishes.*/
+GPRAPI void gpr_once_init(gpr_once *once, void (*init_routine)(void));
+
+/* --- One-time event notification ---
+
+  These operations act on a gpr_event, which should be initialized with
+  gpr_ev_init(), or with GPR_EVENT_INIT if static, e.g.,
+       static gpr_event event_var = GPR_EVENT_INIT;
+  It requires no destruction.  */
+
+/* Initialize *ev. */
+GPRAPI void gpr_event_init(gpr_event *ev);
+
+/* Set *ev so that gpr_event_get() and gpr_event_wait() will return value.
+   Requires:  *ev initialized; value != NULL; no prior or concurrent calls to
+   gpr_event_set(ev, ...) since initialization.  */
+GPRAPI void gpr_event_set(gpr_event *ev, void *value);
+
+/* Return the value set by gpr_event_set(ev, ...), or NULL if no such call has
+   completed.  If the result is non-NULL, all operations that occurred prior to
+   the gpr_event_set(ev, ...) set will be visible after this call returns.
+   Requires:  *ev initialized.  This operation is faster than acquiring a mutex
+   on most platforms.  */
+GPRAPI void *gpr_event_get(gpr_event *ev);
+
+/* Wait until *ev is set by gpr_event_set(ev, ...), or abs_deadline is
+   exceeded, then return gpr_event_get(ev).  Requires:  *ev initialized.  Use
+   abs_deadline==gpr_inf_future for no deadline.  When the event has been
+   signalled before the call, this operation is faster than acquiring a mutex
+   on most platforms.  */
+GPRAPI void *gpr_event_wait(gpr_event *ev, gpr_timespec abs_deadline);
+
+/* --- Reference counting ---
+
+   These calls act on the type gpr_refcount.  It requires no destruction.  */
+
+/* Initialize *r to value n.  */
+GPRAPI void gpr_ref_init(gpr_refcount *r, int n);
+
+/* Increment the reference count *r.  Requires *r initialized. */
+GPRAPI void gpr_ref(gpr_refcount *r);
+
+/* Increment the reference count *r.  Requires *r initialized.
+   Crashes if refcount is zero */
+GPRAPI void gpr_ref_non_zero(gpr_refcount *r);
+
+/* Increment the reference count *r by n.  Requires *r initialized, n > 0. */
+GPRAPI void gpr_refn(gpr_refcount *r, int n);
+
+/* Decrement the reference count *r and return non-zero iff it has reached
+   zero. .  Requires *r initialized. */
+GPRAPI int gpr_unref(gpr_refcount *r);
+
+/* --- Stats counters ---
+
+   These calls act on the integral type gpr_stats_counter.  It requires no
+   destruction.  Static instances may be initialized with
+       gpr_stats_counter c = GPR_STATS_INIT;
+   Beware:  These operations do not imply memory barriers.  Do not use them to
+   synchronize other events.  */
+
+/* Initialize *c to the value n. */
+GPRAPI void gpr_stats_init(gpr_stats_counter *c, intptr_t n);
+
+/* *c += inc.  Requires: *c initialized. */
+GPRAPI void gpr_stats_inc(gpr_stats_counter *c, intptr_t inc);
+
+/* Return *c.  Requires: *c initialized. */
+GPRAPI intptr_t gpr_stats_read(const gpr_stats_counter *c);
+
+/* ==================Example use of interface===================
+   A producer-consumer queue of up to N integers,
+   illustrating the use of the calls in this interface. */
+#if 0
+
+#define N 4
+
+   typedef struct queue {
+     gpr_cv non_empty;  /* Signalled when length becomes non-zero. */
+     gpr_cv non_full;   /* Signalled when length becomes non-N. */
+     gpr_mu mu;         /* Protects all fields below.
+                            (That is, except during initialization or
+                            destruction, the fields below should be accessed
+                            only by a thread that holds mu.) */
+     int head;           /* Index of head of queue 0..N-1. */
+     int length;         /* Number of valid elements in queue 0..N. */
+     int elem[N];        /* elem[head .. head+length-1] are queue elements. */
+   } queue;
+
+   /* Initialize *q. */
+   void queue_init(queue *q) {
+     gpr_mu_init(&q->mu);
+     gpr_cv_init(&q->non_empty);
+     gpr_cv_init(&q->non_full);
+     q->head = 0;
+     q->length = 0;
+   }
+
+   /* Free storage associated with *q. */
+   void queue_destroy(queue *q) {
+     gpr_mu_destroy(&q->mu);
+     gpr_cv_destroy(&q->non_empty);
+     gpr_cv_destroy(&q->non_full);
+   }
+
+   /* Wait until there is room in *q, then append x to *q. */
+   void queue_append(queue *q, int x) {
+     gpr_mu_lock(&q->mu);
+     /* To wait for a predicate without a deadline, loop on the negation of the
+        predicate, and use gpr_cv_wait(..., gpr_inf_future) inside the loop
+        to release the lock, wait, and reacquire on each iteration.  Code that
+        makes the condition true should use gpr_cv_broadcast() on the
+        corresponding condition variable.  The predicate must be on state
+        protected by the lock.  */
+     while (q->length == N) {
+       gpr_cv_wait(&q->non_full, &q->mu, gpr_inf_future);
+     }
+     if (q->length == 0) {  /* Wake threads blocked in queue_remove(). */
+       /* It's normal to use gpr_cv_broadcast() or gpr_signal() while
+          holding the lock. */
+       gpr_cv_broadcast(&q->non_empty);
+     }
+     q->elem[(q->head + q->length) % N] = x;
+     q->length++;
+     gpr_mu_unlock(&q->mu);
+   }
+
+   /* If it can be done without blocking, append x to *q and return non-zero.
+      Otherwise return 0. */
+   int queue_try_append(queue *q, int x) {
+     int result = 0;
+     if (gpr_mu_trylock(&q->mu)) {
+       if (q->length != N) {
+         if (q->length == 0) {  /* Wake threads blocked in queue_remove(). */
+           gpr_cv_broadcast(&q->non_empty);
+         }
+         q->elem[(q->head + q->length) % N] = x;
+         q->length++;
+         result = 1;
+       }
+       gpr_mu_unlock(&q->mu);
+     }
+     return result;
+   }
+
+   /* Wait until the *q is non-empty or deadline abs_deadline passes.  If the
+      queue is non-empty, remove its head entry, place it in *head, and return
+      non-zero.  Otherwise return 0.  */
+   int queue_remove(queue *q, int *head, gpr_timespec abs_deadline) {
+     int result = 0;
+     gpr_mu_lock(&q->mu);
+     /* To wait for a predicate with a deadline, loop on the negation of the
+        predicate or until gpr_cv_wait() returns true.  Code that makes
+        the condition true should use gpr_cv_broadcast() on the corresponding
+        condition variable.  The predicate must be on state protected by the
+        lock. */
+     while (q->length == 0 &&
+            !gpr_cv_wait(&q->non_empty, &q->mu, abs_deadline)) {
+     }
+     if (q->length != 0) {    /* Queue is non-empty. */
+       result = 1;
+       if (q->length == N) {  /* Wake threads blocked in queue_append(). */
+         gpr_cv_broadcast(&q->non_full);
+       }
+       *head = q->elem[q->head];
+       q->head = (q->head + 1) % N;
+       q->length--;
+     } /* else deadline exceeded */
+     gpr_mu_unlock(&q->mu);
+     return result;
+   }
+#endif /* 0 */
+
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* GRPC_SUPPORT_SYNC_H */
diff --git a/include/grpc/support/time.h b/include/grpc/support/time.h
index b3f2f8c..66bcfca 100644
--- a/include/grpc/support/time.h
+++ b/include/grpc/support/time.h
@@ -34,6 +34,72 @@
 #ifndef GRPC_SUPPORT_TIME_H
 #define GRPC_SUPPORT_TIME_H
 
-#include <grpc/impl/codegen/time.h>
+#include <grpc/impl/codegen/gpr_types.h>
+
+#include <stddef.h>
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Time constants. */
+GPRAPI gpr_timespec
+gpr_time_0(gpr_clock_type type); /* The zero time interval. */
+GPRAPI gpr_timespec gpr_inf_future(gpr_clock_type type); /* The far future */
+GPRAPI gpr_timespec gpr_inf_past(gpr_clock_type type);   /* The far past. */
+
+#define GPR_MS_PER_SEC 1000
+#define GPR_US_PER_SEC 1000000
+#define GPR_NS_PER_SEC 1000000000
+#define GPR_NS_PER_MS 1000000
+#define GPR_NS_PER_US 1000
+#define GPR_US_PER_MS 1000
+
+/* initialize time subsystem */
+GPRAPI void gpr_time_init(void);
+
+/* Return the current time measured from the given clocks epoch. */
+GPRAPI gpr_timespec gpr_now(gpr_clock_type clock);
+
+/* Convert a timespec from one clock to another */
+GPRAPI gpr_timespec gpr_convert_clock_type(gpr_timespec t,
+                                           gpr_clock_type target_clock);
+
+/* Return -ve, 0, or +ve according to whether a < b, a == b, or a > b
+   respectively.  */
+GPRAPI int gpr_time_cmp(gpr_timespec a, gpr_timespec b);
+
+GPRAPI gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b);
+GPRAPI gpr_timespec gpr_time_min(gpr_timespec a, gpr_timespec b);
+
+/* Add and subtract times.  Calculations saturate at infinities. */
+GPRAPI gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b);
+GPRAPI gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b);
+
+/* Return a timespec representing a given number of time units. INT64_MIN is
+   interpreted as gpr_inf_past, and INT64_MAX as gpr_inf_future.  */
+GPRAPI gpr_timespec gpr_time_from_micros(int64_t x, gpr_clock_type clock_type);
+GPRAPI gpr_timespec gpr_time_from_nanos(int64_t x, gpr_clock_type clock_type);
+GPRAPI gpr_timespec gpr_time_from_millis(int64_t x, gpr_clock_type clock_type);
+GPRAPI gpr_timespec gpr_time_from_seconds(int64_t x, gpr_clock_type clock_type);
+GPRAPI gpr_timespec gpr_time_from_minutes(int64_t x, gpr_clock_type clock_type);
+GPRAPI gpr_timespec gpr_time_from_hours(int64_t x, gpr_clock_type clock_type);
+
+GPRAPI int32_t gpr_time_to_millis(gpr_timespec timespec);
+
+/* Return 1 if two times are equal or within threshold of each other,
+   0 otherwise */
+GPRAPI int gpr_time_similar(gpr_timespec a, gpr_timespec b,
+                            gpr_timespec threshold);
+
+/* Sleep until at least 'until' - an absolute timeout */
+GPRAPI void gpr_sleep_until(gpr_timespec until);
+
+GPRAPI double gpr_timespec_to_micros(gpr_timespec t);
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* GRPC_SUPPORT_TIME_H */
diff --git a/package.json b/package.json
index 0e229c9..9afba31 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
   "devDependencies": {
     "async": "^1.5.0",
     "google-auth-library": "^0.9.2",
-    "google-protobuf": "^3.0.0-alpha.5",
+    "google-protobuf": "^3.0.0",
     "istanbul": "^0.3.21",
     "jsdoc": "^3.3.2",
     "jshint": "^2.5.0",
diff --git a/package.xml b/package.xml
index f395334..7bac695 100644
--- a/package.xml
+++ b/package.xml
@@ -10,11 +10,11 @@
   <email>grpc-packages@google.com</email>
   <active>yes</active>
  </lead>
- <date>2016-07-28</date>
+ <date>2016-08-22</date>
  <time>16:06:07</time>
  <version>
-  <release>1.1.0</release>
-  <api>1.1.0</api>
+  <release>1.1.0dev</release>
+  <api>1.1.0dev</api>
  </version>
  <stability>
   <release>stable</release>
@@ -22,7 +22,7 @@
  </stability>
  <license>BSD</license>
  <notes>
-- PHP7 Support continued, reduce code duplication #7543
+- Reject metadata keys which are not legal #7881
  </notes>
  <contents>
   <dir baseinstalldir="/" name="/">
@@ -79,25 +79,24 @@
     <file baseinstalldir="/" name="include/grpc/support/tls_msvc.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/support/tls_pthread.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/support/useful.h" role="src" />
-    <file baseinstalldir="/" name="include/grpc/impl/codegen/alloc.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/atm.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/atm_gcc_atomic.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/atm_gcc_sync.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/atm_windows.h" role="src" />
-    <file baseinstalldir="/" name="include/grpc/impl/codegen/log.h" role="src" />
+    <file baseinstalldir="/" name="include/grpc/impl/codegen/gpr_types.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/port_platform.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/slice.h" role="src" />
-    <file baseinstalldir="/" name="include/grpc/impl/codegen/slice_buffer.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/sync.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/sync_generic.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/sync_posix.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/sync_windows.h" role="src" />
-    <file baseinstalldir="/" name="include/grpc/impl/codegen/time.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/profiling/timers.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/backoff.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/block_annotate.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/env.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/support/mpscq.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/murmur_hash.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/support/percent_encoding.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/stack_lockfree.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/string.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/string_windows.h" role="src" />
@@ -124,7 +123,9 @@
     <file baseinstalldir="/" name="src/core/lib/support/log_linux.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/log_posix.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/log_windows.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/support/mpscq.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/murmur_hash.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/support/percent_encoding.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/slice.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/slice_buffer.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/stack_lockfree.c" role="src" />
@@ -154,30 +155,26 @@
     <file baseinstalldir="/" name="include/grpc/compression.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/grpc.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/grpc_posix.h" role="src" />
+    <file baseinstalldir="/" name="include/grpc/grpc_security_constants.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/status.h" role="src" />
-    <file baseinstalldir="/" name="include/grpc/impl/codegen/byte_buffer.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/byte_buffer_reader.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/compression_types.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/connectivity_state.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/grpc_types.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/propagation_bits.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/status.h" role="src" />
-    <file baseinstalldir="/" name="include/grpc/impl/codegen/alloc.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/atm.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/atm_gcc_atomic.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/atm_gcc_sync.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/atm_windows.h" role="src" />
-    <file baseinstalldir="/" name="include/grpc/impl/codegen/log.h" role="src" />
+    <file baseinstalldir="/" name="include/grpc/impl/codegen/gpr_types.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/port_platform.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/slice.h" role="src" />
-    <file baseinstalldir="/" name="include/grpc/impl/codegen/slice_buffer.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/sync.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/sync_generic.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/sync_posix.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/sync_windows.h" role="src" />
-    <file baseinstalldir="/" name="include/grpc/impl/codegen/time.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/grpc_security.h" role="src" />
-    <file baseinstalldir="/" name="include/grpc/grpc_security_constants.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/census.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_args.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_stack.h" role="src" />
@@ -185,9 +182,11 @@
     <file baseinstalldir="/" name="src/core/lib/channel/compress_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/connected_channel.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/context.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/channel/deadline_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/handshaker.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/http_client_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/http_server_filter.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/channel/message_size_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/algorithm_metadata.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/message_compress.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/debug/trace.h" role="src" />
@@ -195,6 +194,7 @@
     <file baseinstalldir="/" name="src/core/lib/http/httpcli.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/http/parser.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/closure.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/combiner.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/error.h" role="src" />
@@ -256,6 +256,7 @@
     <file baseinstalldir="/" name="src/core/lib/transport/metadata.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/metadata_batch.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/static_metadata.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/transport/timeout_encoding.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport_impl.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/bin_decoder.h" role="src" />
@@ -277,7 +278,6 @@
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/internal.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/status_conversion.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/stream_map.h" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/timeout_encoding.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/varint.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/alpn/alpn.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/security/context/security_context.h" role="src" />
@@ -306,8 +306,8 @@
     <file baseinstalldir="/" name="src/core/lib/tsi/transport_security_interface.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/client_channel.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/client_channel_factory.h" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/client_config/client_config.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/connector.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/client_config/http_connect_handshaker.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/initial_connect_string.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/lb_policy.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/lb_policy_factory.h" role="src" />
@@ -316,10 +316,11 @@
     <file baseinstalldir="/" name="src/core/ext/client_config/resolver.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/resolver_factory.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/resolver_registry.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/client_config/resolver_result.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/subchannel.h" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/client_config/subchannel_call_holder.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/subchannel_index.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/uri_parser.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/lb_policy/grpclb/grpclb.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/lb_policy/grpclb/load_balancer_api.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" role="src" />
     <file baseinstalldir="/" name="third_party/nanopb/pb.h" role="src" />
@@ -329,21 +330,27 @@
     <file baseinstalldir="/" name="src/core/ext/load_reporting/load_reporting.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/load_reporting/load_reporting_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/aggregation.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/census/base_resources.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/census_interface.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/census_rpc_stats.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/gen/census.pb.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/census/gen/trace_context.pb.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/grpc_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/mlog.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/census/resource.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/rpc_metric_id.h" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/census/trace_context.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/init.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_args.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_stack.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_stack_builder.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/compress_filter.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/connected_channel.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/channel/deadline_filter.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/handshaker.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/http_client_filter.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/http_server_filter.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/channel/message_size_filter.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/compression.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/message_compress.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/debug/trace.c" role="src" />
@@ -351,6 +358,7 @@
     <file baseinstalldir="/" name="src/core/lib/http/httpcli.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/http/parser.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/closure.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/combiner.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_posix.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_windows.c" role="src" />
@@ -422,6 +430,7 @@
     <file baseinstalldir="/" name="src/core/lib/transport/metadata.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/metadata_batch.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/static_metadata.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/transport/timeout_encoding.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport_op_string.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c" role="src" />
@@ -444,7 +453,6 @@
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/status_conversion.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/stream_lists.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/stream_map.c" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/timeout_encoding.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/varint.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/writing.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/alpn/alpn.c" role="src" />
@@ -480,10 +488,10 @@
     <file baseinstalldir="/" name="src/core/ext/client_config/channel_connectivity.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/client_channel.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/client_channel_factory.c" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/client_config/client_config.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/client_config_plugin.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/connector.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/default_initial_connect_string.c" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/client_config/http_connect_handshaker.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/initial_connect_string.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/lb_policy.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/lb_policy_factory.c" role="src" />
@@ -492,14 +500,15 @@
     <file baseinstalldir="/" name="src/core/ext/client_config/resolver.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/resolver_factory.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/resolver_registry.c" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/client_config/resolver_result.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/subchannel.c" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/client_config/subchannel_call_holder.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/subchannel_index.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/client_config/uri_parser.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/insecure/server_chttp2.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/insecure/channel_create.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/lb_policy/grpclb/grpclb.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/lb_policy/grpclb/load_balancer_api.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c" role="src" />
     <file baseinstalldir="/" name="third_party/nanopb/pb_common.c" role="src" />
@@ -511,8 +520,10 @@
     <file baseinstalldir="/" name="src/core/ext/resolver/sockaddr/sockaddr_resolver.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/load_reporting/load_reporting.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/load_reporting/load_reporting_filter.c" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/census/base_resources.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/context.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/gen/census.pb.c" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/census/gen/trace_context.pb.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/grpc_context.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/grpc_filter.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/grpc_plugin.c" role="src" />
@@ -520,6 +531,8 @@
     <file baseinstalldir="/" name="src/core/ext/census/mlog.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/operation.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/placeholders.c" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/census/resource.c" role="src" />
+    <file baseinstalldir="/" name="src/core/ext/census/trace_context.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/census/tracing.c" role="src" />
     <file baseinstalldir="/" name="src/core/plugin_registry/grpc_plugin_registry.c" role="src" />
     <file baseinstalldir="/" name="third_party/boringssl/crypto/aes/internal.h" role="src" />
@@ -1133,5 +1146,50 @@
 - PHP7 Support continued, reduce code duplication #7543
    </notes>
   </release>
+  <release>
+   <version>
+    <release>1.0.0RC4</release>
+    <api>1.0.0RC4</api>
+   </version>
+   <stability>
+    <release>stable</release>
+    <api>stable</api>
+   </stability>
+   <date>2016-08-09</date>
+   <license>BSD</license>
+   <notes>
+- Fixed Ubuntu compile error #7571, #7642
+   </notes>
+  </release>
+  <release>
+   <version>
+    <release>1.0.0</release>
+    <api>1.0.0</api>
+   </version>
+   <stability>
+    <release>stable</release>
+    <api>stable</api>
+   </stability>
+   <date>2016-08-18</date>
+   <license>BSD</license>
+   <notes>
+- gRPC 1.0.0 release
+   </notes>
+  </release>
+  <release>
+   <version>
+    <release>1.1.0dev</release>
+    <api>1.1.0dev</api>
+   </version>
+   <stability>
+    <release>stable</release>
+    <api>stable</api>
+   </stability>
+   <date>2016-08-22</date>
+   <license>BSD</license>
+   <notes>
+- Reject metadata keys which are not legal #7881
+   </notes>
+  </release>
  </changelog>
 </package>
diff --git a/requirements.txt b/requirements.txt
index 0ec0e75..bf87de0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,6 +3,6 @@
 cython>=0.23
 enum34>=1.0.4
 futures>=2.2.0
-protobuf>=3.0.0a3
+protobuf>=3.0.0
 six>=1.10
-wheel>=0.29
\ No newline at end of file
+wheel>=0.29
diff --git a/setup.py b/setup.py
index cad300c..7a65698 100644
--- a/setup.py
+++ b/setup.py
@@ -28,7 +28,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 """A setup module for the GRPC Python package."""
-
+from distutils import cygwinccompiler
 from distutils import extension as _extension
 from distutils import util
 import os
@@ -58,14 +58,12 @@
 sys.path.insert(0, os.path.abspath(PYTHON_STEM))
 
 # Break import-style to ensure we can actually find our in-repo dependencies.
-import _unixccompiler_patch
+import _spawn_patch
 import commands
 import grpc_core_dependencies
 import grpc_version
 
-if 'win32' in sys.platform:
-  _unixccompiler_patch.monkeypatch_unix_compiler()
-
+_spawn_patch.monkeypatch_spawn()
 
 LICENSE = '3-clause BSD'
 
@@ -91,8 +89,8 @@
 EXTRA_ENV_COMPILE_ARGS = os.environ.get('GRPC_PYTHON_CFLAGS', None)
 EXTRA_ENV_LINK_ARGS = os.environ.get('GRPC_PYTHON_LDFLAGS', None)
 if EXTRA_ENV_COMPILE_ARGS is None:
-  EXTRA_ENV_COMPILE_ARGS = '-fno-wrapv'
-  if 'win32' in sys.platform:
+  EXTRA_ENV_COMPILE_ARGS = ''
+  if 'win32' in sys.platform and sys.version_info < (3, 5):
     # We use define flags here and don't directly add to DEFINE_MACROS below to
     # ensure that the expert user/builder has a way of turning it off (via the
     # envvars) without adding yet more GRPC-specific envvars.
@@ -102,21 +100,21 @@
     else:
       EXTRA_ENV_COMPILE_ARGS += ' -D_ftime=_ftime64 -D_timeb=__timeb64'
   elif "linux" in sys.platform or "darwin" in sys.platform:
-    EXTRA_ENV_COMPILE_ARGS += ' -fvisibility=hidden'
+    EXTRA_ENV_COMPILE_ARGS += ' -fvisibility=hidden -fno-wrapv'
 if EXTRA_ENV_LINK_ARGS is None:
-  EXTRA_ENV_LINK_ARGS = '-lpthread'
-  if 'win32' in sys.platform:
-    # TODO(atash) check if this is actually safe to just import and call on
-    # non-Windows (to avoid breaking import style)
-    from distutils.cygwinccompiler import get_msvcr
-    msvcr = get_msvcr()[0]
+  EXTRA_ENV_LINK_ARGS = ''
+  if "linux" in sys.platform or "darwin" in sys.platform:
+    EXTRA_ENV_LINK_ARGS += ' -lpthread'
+  elif "win32" in sys.platform and sys.version_info < (3, 5):
+    msvcr = cygwinccompiler.get_msvcr()[0]
     # TODO(atash) sift through the GCC specs to see if libstdc++ can have any
     # influence on the linkage outcome on MinGW for non-C++ programs.
     EXTRA_ENV_LINK_ARGS += (
         ' -static-libgcc -static-libstdc++ -mcrtdll={msvcr} '
         '-static'.format(msvcr=msvcr))
-  elif "linux" in sys.platform:
+  if "linux" in sys.platform:
     EXTRA_ENV_LINK_ARGS += ' -Wl,-wrap,memcpy'
+
 EXTRA_COMPILE_ARGS = shlex.split(EXTRA_ENV_COMPILE_ARGS)
 EXTRA_LINK_ARGS = shlex.split(EXTRA_ENV_LINK_ARGS)
 
@@ -137,15 +135,19 @@
 if not "win32" in sys.platform:
   EXTENSION_LIBRARIES += ('m',)
 if "win32" in sys.platform:
-  EXTENSION_LIBRARIES += ('ws2_32',)
+  EXTENSION_LIBRARIES += ('advapi32', 'ws2_32',)
 
 DEFINE_MACROS = (
     ('OPENSSL_NO_ASM', 1), ('_WIN32_WINNT', 0x600),
     ('GPR_BACKWARDS_COMPATIBILITY_MODE', 1),)
 if "win32" in sys.platform:
-  DEFINE_MACROS += (('OPENSSL_WINDOWS', 1), ('WIN32_LEAN_AND_MEAN', 1),)
+  DEFINE_MACROS += (('WIN32_LEAN_AND_MEAN', 1),)
   if '64bit' in platform.architecture()[0]:
     DEFINE_MACROS += (('MS_WIN64', 1),)
+  elif sys.version_info >= (3, 5):
+    # For some reason, this is needed to get access to inet_pton/inet_ntop
+    # on msvc, but only for 32 bits
+    DEFINE_MACROS += (('NTDDI_VERSION', 0x06000000),)
 
 LDFLAGS = tuple(EXTRA_LINK_ARGS)
 CFLAGS = tuple(EXTRA_COMPILE_ARGS)
@@ -154,7 +156,6 @@
   pymodinit = '__attribute__((visibility ("default"))) {}'.format(pymodinit_type)
   DEFINE_MACROS += (('PyMODINIT_FUNC', pymodinit),)
 
-
 # By default, Python3 distutils enforces compatibility of
 # c plugins (.so files) with the OSX version Python3 was built with.
 # For Python3.4, this is OSX 10.6, but we need Thread Local Support (__thread)
@@ -197,12 +198,14 @@
 INSTALL_REQUIRES = (
     'six>=1.5.2',
     'enum34>=1.0.4',
-    'futures>=2.2.0',
     # TODO(atash): eventually split the grpcio package into a metapackage
     # depending on protobuf and the runtime component (independent of protobuf)
-    'protobuf>=3.0.0a3',
+    'protobuf>=3.0.0',
 )
 
+if not PY3:
+  INSTALL_REQUIRES += ('futures>=2.2.0',)
+
 SETUP_REQUIRES = INSTALL_REQUIRES + (
     'sphinx>=1.3',
     'sphinx_rtd_theme>=0.1.8',
diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc
index c386115..d0c35ea 100644
--- a/src/compiler/cpp_generator.cc
+++ b/src/compiler/cpp_generator.cc
@@ -130,6 +130,7 @@
     static const char *headers_strs[] = {
         "grpc++/impl/codegen/async_stream.h",
         "grpc++/impl/codegen/async_unary_call.h",
+        "grpc++/impl/codegen/method_handler_impl.h",
         "grpc++/impl/codegen/proto_utils.h",
         "grpc++/impl/codegen/rpc_method.h",
         "grpc++/impl/codegen/service_type.h",
@@ -604,6 +605,57 @@
   printer->Print(*vars, "};\n");
 }
 
+void PrintHeaderServerMethodStreamedUnary(
+    Printer *printer, const Method *method,
+    std::map<grpc::string, grpc::string> *vars) {
+  (*vars)["Method"] = method->name();
+  (*vars)["Request"] = method->input_type_name();
+  (*vars)["Response"] = method->output_type_name();
+  if (method->NoStreaming()) {
+    printer->Print(*vars, "template <class BaseClass>\n");
+    printer->Print(*vars,
+                   "class WithStreamedUnaryMethod_$Method$ : "
+                   "public BaseClass {\n");
+    printer->Print(
+        " private:\n"
+        "  void BaseClassMustBeDerivedFromService(const Service *service) "
+        "{}\n");
+    printer->Print(" public:\n");
+    printer->Indent();
+    printer->Print(*vars,
+                   "WithStreamedUnaryMethod_$Method$() {\n"
+                   "  ::grpc::Service::MarkMethodStreamedUnary($Idx$,\n"
+                   "    new ::grpc::StreamedUnaryHandler< $Request$, "
+                   "$Response$>(std::bind"
+                   "(&WithStreamedUnaryMethod_$Method$<BaseClass>::"
+                   "Streamed$Method$, this, std::placeholders::_1, "
+                   "std::placeholders::_2)));\n"
+                   "}\n");
+    printer->Print(*vars,
+                   "~WithStreamedUnaryMethod_$Method$() GRPC_OVERRIDE {\n"
+                   "  BaseClassMustBeDerivedFromService(this);\n"
+                   "}\n");
+    printer->Print(
+        *vars,
+        "// disable regular version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, const $Request$* request, "
+        "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+    printer->Print(*vars,
+                   "// replace default version of method with streamed unary\n"
+                   "virtual ::grpc::Status Streamed$Method$("
+                   "::grpc::ServerContext* context, "
+                   "::grpc::ServerUnaryStreamer< "
+                   "$Request$,$Response$>* server_unary_streamer)"
+                   " = 0;\n");
+    printer->Outdent();
+    printer->Print(*vars, "};\n");
+  }
+}
+
 void PrintHeaderServerMethodGeneric(
     Printer *printer, const Method *method,
     std::map<grpc::string, grpc::string> *vars) {
@@ -770,6 +822,28 @@
     PrintHeaderServerMethodGeneric(printer, service->method(i).get(), vars);
   }
 
+  // Server side - Streamed Unary
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["Idx"] = as_string(i);
+    PrintHeaderServerMethodStreamedUnary(printer, service->method(i).get(),
+                                         vars);
+  }
+
+  printer->Print("typedef ");
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["method_name"] = service->method(i).get()->name();
+    if (service->method(i)->NoStreaming()) {
+      printer->Print(*vars, "WithStreamedUnaryMethod_$method_name$<");
+    }
+  }
+  printer->Print("Service");
+  for (int i = 0; i < service->method_count(); ++i) {
+    if (service->method(i)->NoStreaming()) {
+      printer->Print(" >");
+    }
+  }
+  printer->Print(" StreamedUnaryService;\n");
+
   printer->Outdent();
   printer->Print("};\n");
   printer->Print(service->GetTrailingComments().c_str());
@@ -1080,6 +1154,9 @@
     (*vars)["Idx"] = as_string(i);
     if (method->NoStreaming()) {
       (*vars)["StreamingType"] = "NORMAL_RPC";
+      // NOTE: There is no reason to consider streamed-unary as a separate
+      // category here since this part is setting up the client-side stub
+      // and this appears as a NORMAL_RPC from the client-side.
     } else if (method->ClientOnlyStreaming()) {
       (*vars)["StreamingType"] = "CLIENT_STREAMING";
     } else if (method->ServerOnlyStreaming()) {
diff --git a/src/compiler/cpp_generator.h b/src/compiler/cpp_generator.h
index 2a003b1..d0343e9 100644
--- a/src/compiler/cpp_generator.h
+++ b/src/compiler/cpp_generator.h
@@ -41,6 +41,8 @@
 #include <memory>
 #include <vector>
 
+#include "src/compiler/config.h"
+
 #ifndef GRPC_CUSTOM_STRING
 #include <string>
 #define GRPC_CUSTOM_STRING std::string
diff --git a/src/compiler/node_generator.cc b/src/compiler/node_generator.cc
index c385202..d7af125 100644
--- a/src/compiler/node_generator.cc
+++ b/src/compiler/node_generator.cc
@@ -114,8 +114,8 @@
       const MethodDescriptor *method = service->method(method_num);
       const Descriptor *input_type = method->input_type();
       const Descriptor *output_type = method->output_type();
-      message_types[input_type->name()] = input_type;
-      message_types[output_type->name()] = output_type;
+      message_types[input_type->full_name()] = input_type;
+      message_types[output_type->full_name()] = output_type;
     }
   }
   return message_types;
@@ -127,7 +127,7 @@
 
 grpc::string NodeObjectPath(const Descriptor *descriptor) {
   grpc::string module_alias = ModuleAlias(descriptor->file()->name());
-  grpc::string name = descriptor->name();
+  grpc::string name = descriptor->full_name();
   grpc_generator::StripPrefix(&name, descriptor->file()->package() + ".");
   return module_alias + "." + name;
 }
@@ -135,8 +135,9 @@
 // Prints out the message serializer and deserializer functions
 void PrintMessageTransformer(const Descriptor *descriptor, Printer *out) {
   map<grpc::string, grpc::string> template_vars;
-  template_vars["identifier_name"] = MessageIdentifierName(descriptor->name());
-  template_vars["name"] = descriptor->name();
+  grpc::string full_name = descriptor->full_name();
+  template_vars["identifier_name"] = MessageIdentifierName(full_name);
+  template_vars["name"] = full_name;
   template_vars["node_name"] = NodeObjectPath(descriptor);
   // Print the serializer
   out->Print(template_vars, "function serialize_$identifier_name$(arg) {\n");
@@ -169,9 +170,9 @@
   vars["service_name"] = method->service()->full_name();
   vars["name"] = method->name();
   vars["input_type"] = NodeObjectPath(input_type);
-  vars["input_type_id"] = MessageIdentifierName(input_type->name());
+  vars["input_type_id"] = MessageIdentifierName(input_type->full_name());
   vars["output_type"] = NodeObjectPath(output_type);
-  vars["output_type_id"] = MessageIdentifierName(output_type->name());
+  vars["output_type_id"] = MessageIdentifierName(output_type->full_name());
   vars["client_stream"] = method->client_streaming() ? "true" : "false";
   vars["server_stream"] = method->server_streaming() ? "true" : "false";
   out->Print("{\n");
diff --git a/src/compiler/php_generator.cc b/src/compiler/php_generator.cc
new file mode 100644
index 0000000..5dac02c
--- /dev/null
+++ b/src/compiler/php_generator.cc
@@ -0,0 +1,174 @@
+/*
+ *
+ * Copyright 2016, 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 <map>
+
+#include "src/compiler/config.h"
+#include "src/compiler/generator_helpers.h"
+#include "src/compiler/php_generator_helpers.h"
+
+using grpc::protobuf::FileDescriptor;
+using grpc::protobuf::ServiceDescriptor;
+using grpc::protobuf::MethodDescriptor;
+using grpc::protobuf::Descriptor;
+using grpc::protobuf::io::Printer;
+using grpc::protobuf::io::StringOutputStream;
+using std::map;
+
+namespace grpc_php_generator {
+namespace {
+
+grpc::string MessageIdentifierName(const grpc::string &name) {
+  std::vector<grpc::string> tokens = grpc_generator::tokenize(name, ".");
+  std::ostringstream oss;
+  for (unsigned int i = 0; i < tokens.size(); i++) {
+    oss << (i == 0 ? "" : "\\")
+        << grpc_generator::CapitalizeFirstLetter(tokens[i]);
+  }
+  return oss.str();
+}
+
+void PrintMethod(const MethodDescriptor *method, Printer *out) {
+  const Descriptor *input_type = method->input_type();
+  const Descriptor *output_type = method->output_type();
+  map<grpc::string, grpc::string> vars;
+  vars["service_name"] = method->service()->full_name();
+  vars["name"] = method->name();
+  vars["input_type_id"] = MessageIdentifierName(input_type->full_name());
+  vars["output_type_id"] = MessageIdentifierName(output_type->full_name());
+
+  out->Print("/**\n");
+  out->Print(GetPHPComments(method, " *").c_str());
+  if (method->client_streaming()) {
+    out->Print(vars,
+               " * @param array $$metadata metadata\n"
+               " * @param array $$options call options\n */\n"
+               "public function $name$($$metadata = [], "
+               "$$options = []) {\n");
+    out->Indent();
+    if (method->server_streaming()) {
+      out->Print("return $$this->_bidiRequest(");
+    } else {
+      out->Print("return $$this->_clientStreamRequest(");
+    }
+    out->Print(vars,
+               "'/$service_name$/$name$',\n"
+               "['\\$output_type_id$','decode'],\n"
+               "$$metadata, $$options);\n");
+  } else {
+    out->Print(vars,
+               " * @param \\$input_type_id$ $$argument input argument\n"
+               " * @param array $$metadata metadata\n"
+               " * @param array $$options call options\n */\n"
+               "public function $name$(\\$input_type_id$ $$argument,\n"
+               "  $$metadata = [], $$options = []) {\n");
+    out->Indent();
+    if (method->server_streaming()) {
+      out->Print("return $$this->_serverStreamRequest(");
+    } else {
+      out->Print("return $$this->_simpleRequest(");
+    }
+    out->Print(vars,
+               "'/$service_name$/$name$',\n"
+               "$$argument,\n"
+               "['\\$output_type_id$', 'decode'],\n"
+               "$$metadata, $$options);\n");
+  }
+  out->Outdent();
+  out->Print("}\n\n");
+}
+
+// Prints out the service descriptor object
+void PrintService(const ServiceDescriptor *service, Printer *out) {
+  map<grpc::string, grpc::string> vars;
+  out->Print(GetPHPComments(service, "//").c_str());
+  vars["name"] = service->name();
+  out->Print(vars, "class $name$Client extends \\Grpc\\BaseStub {\n\n");
+  out->Indent();
+  out->Print(
+      "/**\n * @param string $$hostname hostname\n"
+      " * @param array $$opts channel options\n"
+      " * @param Grpc\\Channel $$channel (optional) re-use channel "
+      "object\n */\n"
+      "public function __construct($$hostname, $$opts, "
+      "$$channel = null) {\n");
+  out->Indent();
+  out->Print("parent::__construct($$hostname, $$opts, $$channel);\n");
+  out->Outdent();
+  out->Print("}\n\n");
+  for (int i = 0; i < service->method_count(); i++) {
+    grpc::string method_name =
+        grpc_generator::LowercaseFirstLetter(service->method(i)->name());
+    PrintMethod(service->method(i), out);
+  }
+  out->Outdent();
+  out->Print("}\n\n");
+}
+
+void PrintServices(const FileDescriptor *file, Printer *out) {
+  map<grpc::string, grpc::string> vars;
+  vars["package"] = MessageIdentifierName(file->package());
+  out->Print(vars, "namespace $package$ {\n\n");
+  out->Indent();
+  for (int i = 0; i < file->service_count(); i++) {
+    PrintService(file->service(i), out);
+  }
+  out->Outdent();
+  out->Print("}\n");
+}
+}
+
+grpc::string GenerateFile(const FileDescriptor *file) {
+  grpc::string output;
+  {
+    StringOutputStream output_stream(&output);
+    Printer out(&output_stream, '$');
+
+    if (file->service_count() == 0) {
+      return output;
+    }
+    out.Print("<?php\n");
+    out.Print("// GENERATED CODE -- DO NOT EDIT!\n\n");
+
+    grpc::string leading_comments = GetPHPComments(file, "//");
+    if (!leading_comments.empty()) {
+      out.Print("// Original file comments:\n");
+      out.Print(leading_comments.c_str());
+    }
+
+    PrintServices(file, &out);
+  }
+  return output;
+}
+
+}  // namespace grpc_php_generator
diff --git a/src/compiler/php_generator.h b/src/compiler/php_generator.h
new file mode 100644
index 0000000..905dc90
--- /dev/null
+++ b/src/compiler/php_generator.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2016, 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_COMPILER_PHP_GENERATOR_H
+#define GRPC_INTERNAL_COMPILER_PHP_GENERATOR_H
+
+#include "src/compiler/config.h"
+
+namespace grpc_php_generator {
+
+grpc::string GenerateFile(const grpc::protobuf::FileDescriptor *file);
+
+}  // namespace grpc_php_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_PHP_GENERATOR_H
diff --git a/src/compiler/php_generator_helpers.h b/src/compiler/php_generator_helpers.h
new file mode 100644
index 0000000..61c4d21
--- /dev/null
+++ b/src/compiler/php_generator_helpers.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2016, 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_COMPILER_PHP_GENERATOR_HELPERS_H
+#define GRPC_INTERNAL_COMPILER_PHP_GENERATOR_HELPERS_H
+
+#include <algorithm>
+
+#include "src/compiler/config.h"
+#include "src/compiler/generator_helpers.h"
+
+namespace grpc_php_generator {
+
+inline grpc::string GetPHPServiceFilename(const grpc::string& filename) {
+  return grpc_generator::StripProto(filename) + "_grpc_pb.php";
+}
+
+// Get leading or trailing comments in a string. Comment lines start with "// ".
+// Leading detached comments are put in in front of leading comments.
+template <typename DescriptorType>
+inline grpc::string GetPHPComments(const DescriptorType* desc,
+                                   grpc::string prefix) {
+  return grpc_generator::GetPrefixedComments(desc, true, prefix);
+}
+
+}  // namespace grpc_php_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_PHP_GENERATOR_HELPERS_H
diff --git a/src/compiler/php_plugin.cc b/src/compiler/php_plugin.cc
new file mode 100644
index 0000000..88acad6
--- /dev/null
+++ b/src/compiler/php_plugin.cc
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2016, 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.
+ *
+ */
+
+// Generates PHP gRPC service interface out of Protobuf IDL.
+
+#include <memory>
+
+#include "src/compiler/config.h"
+#include "src/compiler/php_generator.h"
+#include "src/compiler/php_generator_helpers.h"
+
+using grpc_php_generator::GenerateFile;
+using grpc_php_generator::GetPHPServiceFilename;
+
+class PHPGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
+ public:
+  PHPGrpcGenerator() {}
+  ~PHPGrpcGenerator() {}
+
+  bool Generate(const grpc::protobuf::FileDescriptor *file,
+                const grpc::string &parameter,
+                grpc::protobuf::compiler::GeneratorContext *context,
+                grpc::string *error) const {
+    grpc::string code = GenerateFile(file);
+    if (code.size() == 0) {
+      return true;
+    }
+
+    // Get output file name
+    grpc::string file_name = GetPHPServiceFilename(file->name());
+
+    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> output(
+        context->Open(file_name));
+    grpc::protobuf::io::CodedOutputStream coded_out(output.get());
+    coded_out.WriteRaw(code.data(), code.size());
+    return true;
+  }
+};
+
+int main(int argc, char *argv[]) {
+  PHPGrpcGenerator generator;
+  return grpc::protobuf::compiler::PluginMain(argc, argv, &generator);
+}
diff --git a/src/compiler/python_generator.cc b/src/compiler/python_generator.cc
index 0f61b1f..6830f49 100644
--- a/src/compiler/python_generator.cc
+++ b/src/compiler/python_generator.cc
@@ -234,6 +234,13 @@
              service->name());
   {
     IndentScope raii_class_indent(out);
+    out->Print(
+        "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
+        "\nIt is recommended to use the GA API (classes and functions in this\n"
+        "file not marked beta) for all further purposes. This class was "
+        "generated\n"
+        "only to ease transition from grpcio<0.15.0 to "
+        "grpcio>=0.15.0.\"\"\"\n");
     PrintAllComments(service, out);
     for (int i = 0; i < service->method_count(); ++i) {
       auto meth = service->method(i);
@@ -256,6 +263,13 @@
   out->Print("class Beta$Service$Stub(object):\n", "Service", service->name());
   {
     IndentScope raii_class_indent(out);
+    out->Print(
+        "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
+        "\nIt is recommended to use the GA API (classes and functions in this\n"
+        "file not marked beta) for all further purposes. This class was "
+        "generated\n"
+        "only to ease transition from grpcio<0.15.0 to "
+        "grpcio>=0.15.0.\"\"\"\n");
     PrintAllComments(service, out);
     for (int i = 0; i < service->method_count(); ++i) {
       const MethodDescriptor* meth = service->method(i);
@@ -287,6 +301,12 @@
       "Service", service->name());
   {
     IndentScope raii_create_server_indent(out);
+    out->Print(
+        "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
+        "\nIt is recommended to use the GA API (classes and functions in this\n"
+        "file not marked beta) for all further purposes. This function was\n"
+        "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"
+        "\"\"\"\n");
     map<grpc::string, grpc::string> method_implementation_constructors;
     map<grpc::string, grpc::string> input_message_modules_and_classes;
     map<grpc::string, grpc::string> output_message_modules_and_classes;
@@ -386,6 +406,12 @@
              " metadata_transformer=None, pool=None, pool_size=None):\n");
   {
     IndentScope raii_create_server_indent(out);
+    out->Print(
+        "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n"
+        "\nIt is recommended to use the GA API (classes and functions in this\n"
+        "file not marked beta) for all further purposes. This function was\n"
+        "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"
+        "\"\"\"\n");
     map<grpc::string, grpc::string> method_cardinalities;
     map<grpc::string, grpc::string> input_message_modules_and_classes;
     map<grpc::string, grpc::string> output_message_modules_and_classes;
diff --git a/src/core/ext/census/base_resources.c b/src/core/ext/census/base_resources.c
new file mode 100644
index 0000000..f9aa4bb
--- /dev/null
+++ b/src/core/ext/census/base_resources.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016, 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/ext/census/base_resources.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/census.h>
+#include <grpc/support/log.h>
+
+#include "src/core/ext/census/resource.h"
+
+// Add base RPC resource definitions for use by RPC runtime.
+//
+// TODO(aveitch): All of these are currently hardwired definitions encoded in
+// the code in this file. These should be converted to use an external
+// configuration mechanism, in which these resources are defined in a text
+// file, which is compiled to .pb format and read by still-to-be-written
+// configuration functions.
+
+// Define all base resources. This should be called by census initialization.
+void define_base_resources() {
+  google_census_Resource_BasicUnit numerator =
+      google_census_Resource_BasicUnit_SECS;
+  resource r = {"client_rpc_latency",             // name
+                "Client RPC latency in seconds",  // description
+                0,                                // prefix
+                1,                                // n_numerators
+                &numerator,                       // numerators
+                0,                                // n_denominators
+                NULL};                            // denominators
+  define_resource(&r);
+  r = (resource){"server_rpc_latency",             // name
+                 "Server RPC latency in seconds",  // description
+                 0,                                // prefix
+                 1,                                // n_numerators
+                 &numerator,                       // numerators
+                 0,                                // n_denominators
+                 NULL};                            // denominators
+  define_resource(&r);
+}
diff --git a/src/core/ext/census/base_resources.h b/src/core/ext/census/base_resources.h
new file mode 100644
index 0000000..e5a7696
--- /dev/null
+++ b/src/core/ext/census/base_resources.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016, 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_CORE_EXT_CENSUS_BASE_RESOURCES_H
+#define GRPC_CORE_EXT_CENSUS_BASE_RESOURCES_H
+
+/* Define all base resources. This should be called by census initialization. */
+void define_base_resources();
+
+#endif /* GRPC_CORE_EXT_CENSUS_BASE_RESOURCES_H */
diff --git a/src/core/ext/census/gen/README.md b/src/core/ext/census/gen/README.md
index 72bef65..fdbac10 100644
--- a/src/core/ext/census/gen/README.md
+++ b/src/core/ext/census/gen/README.md
@@ -4,3 +4,7 @@
 * census.pb.{h,c} - Generated from src/core/ext/census/census.proto, using the
   script `tools/codegen/core/gen_nano_proto.sh src/proto/census/census.proto
   $PWD/src/core/ext/census/gen src/core/ext/census/gen`
+* trace_context.pb.{h,c} - Generated from
+  src/core/ext/census/trace_context.proto, using the script
+  `tools/codegen/core/gen_nano_proto.sh src/proto/census/trace_context.proto
+  $PWD/src/core/ext/census/gen src/core/ext/census/gen`
diff --git a/src/core/ext/census/gen/census.pb.c b/src/core/ext/census/gen/census.pb.c
index d614636..647f063 100644
--- a/src/core/ext/census/gen/census.pb.c
+++ b/src/core/ext/census/gen/census.pb.c
@@ -53,29 +53,24 @@
     PB_LAST_FIELD
 };
 
-const pb_field_t google_census_Metric_fields[5] = {
-    PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, google_census_Metric, name, name, 0),
-    PB_FIELD(  2, STRING  , OPTIONAL, CALLBACK, OTHER, google_census_Metric, description, name, 0),
-    PB_FIELD(  3, MESSAGE , OPTIONAL, STATIC  , OTHER, google_census_Metric, unit, description, &google_census_Metric_MeasurementUnit_fields),
-    PB_FIELD(  4, INT32   , OPTIONAL, STATIC  , OTHER, google_census_Metric, id, unit, 0),
+const pb_field_t google_census_Resource_fields[4] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, google_census_Resource, name, name, 0),
+    PB_FIELD(  2, STRING  , OPTIONAL, CALLBACK, OTHER, google_census_Resource, description, name, 0),
+    PB_FIELD(  3, MESSAGE , OPTIONAL, STATIC  , OTHER, google_census_Resource, unit, description, &google_census_Resource_MeasurementUnit_fields),
     PB_LAST_FIELD
 };
 
-const pb_field_t google_census_Metric_BasicUnit_fields[2] = {
-    PB_FIELD(  1, UENUM   , OPTIONAL, STATIC  , FIRST, google_census_Metric_BasicUnit, type, type, 0),
+const pb_field_t google_census_Resource_MeasurementUnit_fields[4] = {
+    PB_FIELD(  1, INT32   , OPTIONAL, STATIC  , FIRST, google_census_Resource_MeasurementUnit, prefix, prefix, 0),
+    PB_FIELD(  2, UENUM   , REPEATED, CALLBACK, OTHER, google_census_Resource_MeasurementUnit, numerator, prefix, 0),
+    PB_FIELD(  3, UENUM   , REPEATED, CALLBACK, OTHER, google_census_Resource_MeasurementUnit, denominator, numerator, 0),
     PB_LAST_FIELD
 };
 
-const pb_field_t google_census_Metric_MeasurementUnit_fields[4] = {
-    PB_FIELD(  1, INT32   , OPTIONAL, STATIC  , FIRST, google_census_Metric_MeasurementUnit, prefix, prefix, 0),
-    PB_FIELD(  2, MESSAGE , REPEATED, CALLBACK, OTHER, google_census_Metric_MeasurementUnit, numerator, prefix, &google_census_Metric_BasicUnit_fields),
-    PB_FIELD(  3, MESSAGE , REPEATED, CALLBACK, OTHER, google_census_Metric_MeasurementUnit, denominator, numerator, &google_census_Metric_BasicUnit_fields),
-    PB_LAST_FIELD
-};
-
-const pb_field_t google_census_AggregationDescriptor_fields[3] = {
-    PB_ONEOF_FIELD(options,   1, MESSAGE , ONEOF, STATIC  , FIRST, google_census_AggregationDescriptor, bucket_boundaries, bucket_boundaries, &google_census_AggregationDescriptor_BucketBoundaries_fields),
-    PB_ONEOF_FIELD(options,   2, MESSAGE , ONEOF, STATIC  , FIRST, google_census_AggregationDescriptor, interval_boundaries, interval_boundaries, &google_census_AggregationDescriptor_IntervalBoundaries_fields),
+const pb_field_t google_census_AggregationDescriptor_fields[4] = {
+    PB_FIELD(  1, UENUM   , OPTIONAL, STATIC  , FIRST, google_census_AggregationDescriptor, type, type, 0),
+    PB_ONEOF_FIELD(options,   2, MESSAGE , ONEOF, STATIC  , OTHER, google_census_AggregationDescriptor, bucket_boundaries, type, &google_census_AggregationDescriptor_BucketBoundaries_fields),
+    PB_ONEOF_FIELD(options,   3, MESSAGE , ONEOF, STATIC  , OTHER, google_census_AggregationDescriptor, interval_boundaries, type, &google_census_AggregationDescriptor_IntervalBoundaries_fields),
     PB_LAST_FIELD
 };
 
@@ -124,25 +119,27 @@
 const pb_field_t google_census_View_fields[6] = {
     PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, google_census_View, name, name, 0),
     PB_FIELD(  2, STRING  , OPTIONAL, CALLBACK, OTHER, google_census_View, description, name, 0),
-    PB_FIELD(  3, INT32   , OPTIONAL, STATIC  , OTHER, google_census_View, metric_id, description, 0),
-    PB_FIELD(  4, MESSAGE , OPTIONAL, STATIC  , OTHER, google_census_View, aggregation, metric_id, &google_census_AggregationDescriptor_fields),
+    PB_FIELD(  3, STRING  , OPTIONAL, CALLBACK, OTHER, google_census_View, resource_name, description, 0),
+    PB_FIELD(  4, MESSAGE , OPTIONAL, STATIC  , OTHER, google_census_View, aggregation, resource_name, &google_census_AggregationDescriptor_fields),
     PB_FIELD(  5, STRING  , REPEATED, CALLBACK, OTHER, google_census_View, tag_key, aggregation, 0),
     PB_LAST_FIELD
 };
 
-const pb_field_t google_census_Aggregation_fields[6] = {
+const pb_field_t google_census_Aggregation_fields[7] = {
     PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, google_census_Aggregation, name, name, 0),
     PB_FIELD(  2, STRING  , OPTIONAL, CALLBACK, OTHER, google_census_Aggregation, description, name, 0),
-    PB_ONEOF_FIELD(data,   3, MESSAGE , ONEOF, STATIC  , OTHER, google_census_Aggregation, distribution, description, &google_census_Distribution_fields),
-    PB_ONEOF_FIELD(data,   4, MESSAGE , ONEOF, STATIC  , OTHER, google_census_Aggregation, interval_stats, description, &google_census_IntervalStats_fields),
-    PB_FIELD(  5, MESSAGE , REPEATED, CALLBACK, OTHER, google_census_Aggregation, tag, data.interval_stats, &google_census_Tag_fields),
+    PB_ONEOF_FIELD(data,   3, UINT64  , ONEOF, STATIC  , OTHER, google_census_Aggregation, count, description, 0),
+    PB_ONEOF_FIELD(data,   4, MESSAGE , ONEOF, STATIC  , OTHER, google_census_Aggregation, distribution, description, &google_census_Distribution_fields),
+    PB_ONEOF_FIELD(data,   5, MESSAGE , ONEOF, STATIC  , OTHER, google_census_Aggregation, interval_stats, description, &google_census_IntervalStats_fields),
+    PB_FIELD(  6, MESSAGE , REPEATED, CALLBACK, OTHER, google_census_Aggregation, tag, data.interval_stats, &google_census_Tag_fields),
     PB_LAST_FIELD
 };
 
-const pb_field_t google_census_ViewAggregations_fields[4] = {
-    PB_FIELD(  1, MESSAGE , REPEATED, CALLBACK, FIRST, google_census_ViewAggregations, aggregation, aggregation, &google_census_Aggregation_fields),
-    PB_FIELD(  2, MESSAGE , OPTIONAL, STATIC  , OTHER, google_census_ViewAggregations, start, aggregation, &google_census_Timestamp_fields),
-    PB_FIELD(  3, MESSAGE , OPTIONAL, STATIC  , OTHER, google_census_ViewAggregations, end, start, &google_census_Timestamp_fields),
+const pb_field_t google_census_Metric_fields[5] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, google_census_Metric, view_name, view_name, 0),
+    PB_FIELD(  2, MESSAGE , REPEATED, CALLBACK, OTHER, google_census_Metric, aggregation, view_name, &google_census_Aggregation_fields),
+    PB_FIELD(  3, MESSAGE , OPTIONAL, STATIC  , OTHER, google_census_Metric, start, aggregation, &google_census_Timestamp_fields),
+    PB_FIELD(  4, MESSAGE , OPTIONAL, STATIC  , OTHER, google_census_Metric, end, start, &google_census_Timestamp_fields),
     PB_LAST_FIELD
 };
 
@@ -156,7 +153,7 @@
  * numbers or field sizes that are larger than what can fit in 8 or 16 bit
  * field descriptors.
  */
-PB_STATIC_ASSERT((pb_membersize(google_census_Metric, unit) < 65536 && pb_membersize(google_census_Metric_MeasurementUnit, numerator) < 65536 && pb_membersize(google_census_Metric_MeasurementUnit, denominator) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Metric, unit) < 65536 && pb_membersize(google_census_Metric_MeasurementUnit, numerator) < 65536 && pb_membersize(google_census_Metric_MeasurementUnit, denominator) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Distribution, range) < 65536 && pb_membersize(google_census_IntervalStats, window) < 65536 && pb_membersize(google_census_IntervalStats_Window, window_size) < 65536 && pb_membersize(google_census_View, aggregation) < 65536 && pb_membersize(google_census_Aggregation, data.distribution) < 65536 && pb_membersize(google_census_Aggregation, data.interval_stats) < 65536 && pb_membersize(google_census_Metric, unit) < 65536 && pb_membersize(google_census_Metric_MeasurementUnit, numerator) < 65536 && pb_membersize(google_census_Metric_MeasurementUnit, denominator) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Metric, unit) < 65536 && pb_membersize(google_census_Metric_MeasurementUnit, numerator) < 65536 && pb_membersize(google_census_Metric_MeasurementUnit, denominator) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Distribution, range) < 65536 && pb_membersize(google_census_IntervalStats, window) < 65536 && pb_membersize(google_census_IntervalStats_Window, window_size) < 65536 && pb_membersize(google_census_View, aggregation) < 65536 && pb_membersize(google_census_Aggregation, data.distribution) < 65536 && pb_membersize(google_census_Aggregation, data.interval_stats) < 65536 && pb_membersize(google_census_Aggregation, tag) < 65536 && pb_membersize(google_census_ViewAggregations, aggregation) < 65536 && pb_membersize(google_census_ViewAggregations, start) < 65536 && pb_membersize(google_census_ViewAggregations, end) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_census_Duration_google_census_Timestamp_google_census_Metric_google_census_Metric_BasicUnit_google_census_Metric_MeasurementUnit_google_census_AggregationDescriptor_google_census_AggregationDescriptor_BucketBoundaries_google_census_AggregationDescriptor_IntervalBoundaries_google_census_Distribution_google_census_Distribution_Range_google_census_IntervalStats_google_census_IntervalStats_Window_google_census_Tag_google_census_View_google_census_Aggregation_google_census_ViewAggregations)
+PB_STATIC_ASSERT((pb_membersize(google_census_Resource, unit) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Resource, unit) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Distribution, range) < 65536 && pb_membersize(google_census_IntervalStats, window) < 65536 && pb_membersize(google_census_IntervalStats_Window, window_size) < 65536 && pb_membersize(google_census_View, aggregation) < 65536 && pb_membersize(google_census_Aggregation, data.distribution) < 65536 && pb_membersize(google_census_Aggregation, data.interval_stats) < 65536 && pb_membersize(google_census_Resource, unit) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Resource, unit) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Distribution, range) < 65536 && pb_membersize(google_census_IntervalStats, window) < 65536 && pb_membersize(google_census_IntervalStats_Window, window_size) < 65536 && pb_membersize(google_census_View, aggregation) < 65536 && pb_membersize(google_census_Aggregation, data.distribution) < 65536 && pb_membersize(google_census_Aggregation, data.interval_stats) < 65536 && pb_membersize(google_census_Aggregation, tag) < 65536 && pb_membersize(google_census_Metric, aggregation) < 65536 && pb_membersize(google_census_Metric, start) < 65536 && pb_membersize(google_census_Metric, end) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_census_Duration_google_census_Timestamp_google_census_Resource_google_census_Resource_MeasurementUnit_google_census_AggregationDescriptor_google_census_AggregationDescriptor_BucketBoundaries_google_census_AggregationDescriptor_IntervalBoundaries_google_census_Distribution_google_census_Distribution_Range_google_census_IntervalStats_google_census_IntervalStats_Window_google_census_Tag_google_census_View_google_census_Aggregation_google_census_Metric)
 #endif
 
 #if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
@@ -167,7 +164,7 @@
  * numbers or field sizes that are larger than what can fit in the default
  * 8 bit descriptors.
  */
-PB_STATIC_ASSERT((pb_membersize(google_census_Metric, unit) < 256 && pb_membersize(google_census_Metric_MeasurementUnit, numerator) < 256 && pb_membersize(google_census_Metric_MeasurementUnit, denominator) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Metric, unit) < 256 && pb_membersize(google_census_Metric_MeasurementUnit, numerator) < 256 && pb_membersize(google_census_Metric_MeasurementUnit, denominator) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Distribution, range) < 256 && pb_membersize(google_census_IntervalStats, window) < 256 && pb_membersize(google_census_IntervalStats_Window, window_size) < 256 && pb_membersize(google_census_View, aggregation) < 256 && pb_membersize(google_census_Aggregation, data.distribution) < 256 && pb_membersize(google_census_Aggregation, data.interval_stats) < 256 && pb_membersize(google_census_Metric, unit) < 256 && pb_membersize(google_census_Metric_MeasurementUnit, numerator) < 256 && pb_membersize(google_census_Metric_MeasurementUnit, denominator) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Metric, unit) < 256 && pb_membersize(google_census_Metric_MeasurementUnit, numerator) < 256 && pb_membersize(google_census_Metric_MeasurementUnit, denominator) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Distribution, range) < 256 && pb_membersize(google_census_IntervalStats, window) < 256 && pb_membersize(google_census_IntervalStats_Window, window_size) < 256 && pb_membersize(google_census_View, aggregation) < 256 && pb_membersize(google_census_Aggregation, data.distribution) < 256 && pb_membersize(google_census_Aggregation, data.interval_stats) < 256 && pb_membersize(google_census_Aggregation, tag) < 256 && pb_membersize(google_census_ViewAggregations, aggregation) < 256 && pb_membersize(google_census_ViewAggregations, start) < 256 && pb_membersize(google_census_ViewAggregations, end) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_census_Duration_google_census_Timestamp_google_census_Metric_google_census_Metric_BasicUnit_google_census_Metric_MeasurementUnit_google_census_AggregationDescriptor_google_census_AggregationDescriptor_BucketBoundaries_google_census_AggregationDescriptor_IntervalBoundaries_google_census_Distribution_google_census_Distribution_Range_google_census_IntervalStats_google_census_IntervalStats_Window_google_census_Tag_google_census_View_google_census_Aggregation_google_census_ViewAggregations)
+PB_STATIC_ASSERT((pb_membersize(google_census_Resource, unit) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Resource, unit) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Distribution, range) < 256 && pb_membersize(google_census_IntervalStats, window) < 256 && pb_membersize(google_census_IntervalStats_Window, window_size) < 256 && pb_membersize(google_census_View, aggregation) < 256 && pb_membersize(google_census_Aggregation, data.distribution) < 256 && pb_membersize(google_census_Aggregation, data.interval_stats) < 256 && pb_membersize(google_census_Resource, unit) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Resource, unit) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Distribution, range) < 256 && pb_membersize(google_census_IntervalStats, window) < 256 && pb_membersize(google_census_IntervalStats_Window, window_size) < 256 && pb_membersize(google_census_View, aggregation) < 256 && pb_membersize(google_census_Aggregation, data.distribution) < 256 && pb_membersize(google_census_Aggregation, data.interval_stats) < 256 && pb_membersize(google_census_Aggregation, tag) < 256 && pb_membersize(google_census_Metric, aggregation) < 256 && pb_membersize(google_census_Metric, start) < 256 && pb_membersize(google_census_Metric, end) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_census_Duration_google_census_Timestamp_google_census_Resource_google_census_Resource_MeasurementUnit_google_census_AggregationDescriptor_google_census_AggregationDescriptor_BucketBoundaries_google_census_AggregationDescriptor_IntervalBoundaries_google_census_Distribution_google_census_Distribution_Range_google_census_IntervalStats_google_census_IntervalStats_Window_google_census_Tag_google_census_View_google_census_Aggregation_google_census_Metric)
 #endif
 
 
diff --git a/src/core/ext/census/gen/census.pb.h b/src/core/ext/census/gen/census.pb.h
index d040fe2..dae583f 100644
--- a/src/core/ext/census/gen/census.pb.h
+++ b/src/core/ext/census/gen/census.pb.h
@@ -45,14 +45,21 @@
 #endif
 
 /* Enum definitions */
-typedef enum _google_census_Metric_BasicUnit_Measure {
-    google_census_Metric_BasicUnit_Measure_UNKNOWN = 0,
-    google_census_Metric_BasicUnit_Measure_BITS = 1,
-    google_census_Metric_BasicUnit_Measure_BYTES = 2,
-    google_census_Metric_BasicUnit_Measure_SECS = 3,
-    google_census_Metric_BasicUnit_Measure_CORES = 4,
-    google_census_Metric_BasicUnit_Measure_MAX_UNITS = 5
-} google_census_Metric_BasicUnit_Measure;
+typedef enum _google_census_Resource_BasicUnit {
+    google_census_Resource_BasicUnit_UNKNOWN = 0,
+    google_census_Resource_BasicUnit_BITS = 1,
+    google_census_Resource_BasicUnit_BYTES = 2,
+    google_census_Resource_BasicUnit_SECS = 3,
+    google_census_Resource_BasicUnit_CORES = 4,
+    google_census_Resource_BasicUnit_MAX_UNITS = 5
+} google_census_Resource_BasicUnit;
+
+typedef enum _google_census_AggregationDescriptor_AggregationType {
+    google_census_AggregationDescriptor_AggregationType_UNKNOWN = 0,
+    google_census_AggregationDescriptor_AggregationType_COUNT = 1,
+    google_census_AggregationDescriptor_AggregationType_DISTRIBUTION = 2,
+    google_census_AggregationDescriptor_AggregationType_INTERVAL = 3
+} google_census_AggregationDescriptor_AggregationType;
 
 /* Struct definitions */
 typedef struct _google_census_AggregationDescriptor_BucketBoundaries {
@@ -68,6 +75,8 @@
 } google_census_IntervalStats;
 
 typedef struct _google_census_AggregationDescriptor {
+    bool has_type;
+    google_census_AggregationDescriptor_AggregationType type;
     pb_size_t which_options;
     union {
         google_census_AggregationDescriptor_BucketBoundaries bucket_boundaries;
@@ -89,17 +98,12 @@
     int32_t nanos;
 } google_census_Duration;
 
-typedef struct _google_census_Metric_BasicUnit {
-    bool has_type;
-    google_census_Metric_BasicUnit_Measure type;
-} google_census_Metric_BasicUnit;
-
-typedef struct _google_census_Metric_MeasurementUnit {
+typedef struct _google_census_Resource_MeasurementUnit {
     bool has_prefix;
     int32_t prefix;
     pb_callback_t numerator;
     pb_callback_t denominator;
-} google_census_Metric_MeasurementUnit;
+} google_census_Resource_MeasurementUnit;
 
 typedef struct _google_census_Tag {
     bool has_key;
@@ -135,37 +139,36 @@
 } google_census_IntervalStats_Window;
 
 typedef struct _google_census_Metric {
-    pb_callback_t name;
-    pb_callback_t description;
-    bool has_unit;
-    google_census_Metric_MeasurementUnit unit;
-    bool has_id;
-    int32_t id;
-} google_census_Metric;
-
-typedef struct _google_census_View {
-    pb_callback_t name;
-    pb_callback_t description;
-    bool has_metric_id;
-    int32_t metric_id;
-    bool has_aggregation;
-    google_census_AggregationDescriptor aggregation;
-    pb_callback_t tag_key;
-} google_census_View;
-
-typedef struct _google_census_ViewAggregations {
+    pb_callback_t view_name;
     pb_callback_t aggregation;
     bool has_start;
     google_census_Timestamp start;
     bool has_end;
     google_census_Timestamp end;
-} google_census_ViewAggregations;
+} google_census_Metric;
+
+typedef struct _google_census_Resource {
+    pb_callback_t name;
+    pb_callback_t description;
+    bool has_unit;
+    google_census_Resource_MeasurementUnit unit;
+} google_census_Resource;
+
+typedef struct _google_census_View {
+    pb_callback_t name;
+    pb_callback_t description;
+    pb_callback_t resource_name;
+    bool has_aggregation;
+    google_census_AggregationDescriptor aggregation;
+    pb_callback_t tag_key;
+} google_census_View;
 
 typedef struct _google_census_Aggregation {
     pb_callback_t name;
     pb_callback_t description;
     pb_size_t which_data;
     union {
+        uint64_t count;
         google_census_Distribution distribution;
         google_census_IntervalStats interval_stats;
     } data;
@@ -177,10 +180,9 @@
 /* Initializer values for message structs */
 #define google_census_Duration_init_default      {false, 0, false, 0}
 #define google_census_Timestamp_init_default     {false, 0, false, 0}
-#define google_census_Metric_init_default        {{{NULL}, NULL}, {{NULL}, NULL}, false, google_census_Metric_MeasurementUnit_init_default, false, 0}
-#define google_census_Metric_BasicUnit_init_default {false, (google_census_Metric_BasicUnit_Measure)0}
-#define google_census_Metric_MeasurementUnit_init_default {false, 0, {{NULL}, NULL}, {{NULL}, NULL}}
-#define google_census_AggregationDescriptor_init_default {0, {google_census_AggregationDescriptor_BucketBoundaries_init_default}}
+#define google_census_Resource_init_default      {{{NULL}, NULL}, {{NULL}, NULL}, false, google_census_Resource_MeasurementUnit_init_default}
+#define google_census_Resource_MeasurementUnit_init_default {false, 0, {{NULL}, NULL}, {{NULL}, NULL}}
+#define google_census_AggregationDescriptor_init_default {false, (google_census_AggregationDescriptor_AggregationType)0, 0, {google_census_AggregationDescriptor_BucketBoundaries_init_default}}
 #define google_census_AggregationDescriptor_BucketBoundaries_init_default {{{NULL}, NULL}}
 #define google_census_AggregationDescriptor_IntervalBoundaries_init_default {{{NULL}, NULL}}
 #define google_census_Distribution_init_default  {false, 0, false, 0, false, google_census_Distribution_Range_init_default, {{NULL}, NULL}}
@@ -188,15 +190,14 @@
 #define google_census_IntervalStats_init_default {{{NULL}, NULL}}
 #define google_census_IntervalStats_Window_init_default {false, google_census_Duration_init_default, false, 0, false, 0}
 #define google_census_Tag_init_default           {false, "", false, ""}
-#define google_census_View_init_default          {{{NULL}, NULL}, {{NULL}, NULL}, false, 0, false, google_census_AggregationDescriptor_init_default, {{NULL}, NULL}}
-#define google_census_Aggregation_init_default   {{{NULL}, NULL}, {{NULL}, NULL}, 0, {google_census_Distribution_init_default}, {{NULL}, NULL}}
-#define google_census_ViewAggregations_init_default {{{NULL}, NULL}, false, google_census_Timestamp_init_default, false, google_census_Timestamp_init_default}
+#define google_census_View_init_default          {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, google_census_AggregationDescriptor_init_default, {{NULL}, NULL}}
+#define google_census_Aggregation_init_default   {{{NULL}, NULL}, {{NULL}, NULL}, 0, {0}, {{NULL}, NULL}}
+#define google_census_Metric_init_default        {{{NULL}, NULL}, {{NULL}, NULL}, false, google_census_Timestamp_init_default, false, google_census_Timestamp_init_default}
 #define google_census_Duration_init_zero         {false, 0, false, 0}
 #define google_census_Timestamp_init_zero        {false, 0, false, 0}
-#define google_census_Metric_init_zero           {{{NULL}, NULL}, {{NULL}, NULL}, false, google_census_Metric_MeasurementUnit_init_zero, false, 0}
-#define google_census_Metric_BasicUnit_init_zero {false, (google_census_Metric_BasicUnit_Measure)0}
-#define google_census_Metric_MeasurementUnit_init_zero {false, 0, {{NULL}, NULL}, {{NULL}, NULL}}
-#define google_census_AggregationDescriptor_init_zero {0, {google_census_AggregationDescriptor_BucketBoundaries_init_zero}}
+#define google_census_Resource_init_zero         {{{NULL}, NULL}, {{NULL}, NULL}, false, google_census_Resource_MeasurementUnit_init_zero}
+#define google_census_Resource_MeasurementUnit_init_zero {false, 0, {{NULL}, NULL}, {{NULL}, NULL}}
+#define google_census_AggregationDescriptor_init_zero {false, (google_census_AggregationDescriptor_AggregationType)0, 0, {google_census_AggregationDescriptor_BucketBoundaries_init_zero}}
 #define google_census_AggregationDescriptor_BucketBoundaries_init_zero {{{NULL}, NULL}}
 #define google_census_AggregationDescriptor_IntervalBoundaries_init_zero {{{NULL}, NULL}}
 #define google_census_Distribution_init_zero     {false, 0, false, 0, false, google_census_Distribution_Range_init_zero, {{NULL}, NULL}}
@@ -204,25 +205,25 @@
 #define google_census_IntervalStats_init_zero    {{{NULL}, NULL}}
 #define google_census_IntervalStats_Window_init_zero {false, google_census_Duration_init_zero, false, 0, false, 0}
 #define google_census_Tag_init_zero              {false, "", false, ""}
-#define google_census_View_init_zero             {{{NULL}, NULL}, {{NULL}, NULL}, false, 0, false, google_census_AggregationDescriptor_init_zero, {{NULL}, NULL}}
-#define google_census_Aggregation_init_zero      {{{NULL}, NULL}, {{NULL}, NULL}, 0, {google_census_Distribution_init_zero}, {{NULL}, NULL}}
-#define google_census_ViewAggregations_init_zero {{{NULL}, NULL}, false, google_census_Timestamp_init_zero, false, google_census_Timestamp_init_zero}
+#define google_census_View_init_zero             {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, google_census_AggregationDescriptor_init_zero, {{NULL}, NULL}}
+#define google_census_Aggregation_init_zero      {{{NULL}, NULL}, {{NULL}, NULL}, 0, {0}, {{NULL}, NULL}}
+#define google_census_Metric_init_zero           {{{NULL}, NULL}, {{NULL}, NULL}, false, google_census_Timestamp_init_zero, false, google_census_Timestamp_init_zero}
 
 /* Field tags (for use in manual encoding/decoding) */
 #define google_census_AggregationDescriptor_BucketBoundaries_bounds_tag 1
 #define google_census_AggregationDescriptor_IntervalBoundaries_window_size_tag 1
 #define google_census_IntervalStats_window_tag   1
-#define google_census_AggregationDescriptor_bucket_boundaries_tag 1
+#define google_census_AggregationDescriptor_bucket_boundaries_tag 2
 
-#define google_census_AggregationDescriptor_interval_boundaries_tag 2
+#define google_census_AggregationDescriptor_interval_boundaries_tag 3
+#define google_census_AggregationDescriptor_type_tag 1
 #define google_census_Distribution_Range_min_tag 1
 #define google_census_Distribution_Range_max_tag 2
 #define google_census_Duration_seconds_tag       1
 #define google_census_Duration_nanos_tag         2
-#define google_census_Metric_BasicUnit_type_tag  1
-#define google_census_Metric_MeasurementUnit_prefix_tag 1
-#define google_census_Metric_MeasurementUnit_numerator_tag 2
-#define google_census_Metric_MeasurementUnit_denominator_tag 3
+#define google_census_Resource_MeasurementUnit_prefix_tag 1
+#define google_census_Resource_MeasurementUnit_numerator_tag 2
+#define google_census_Resource_MeasurementUnit_denominator_tag 3
 #define google_census_Tag_key_tag                1
 #define google_census_Tag_value_tag              2
 #define google_census_Timestamp_seconds_tag      1
@@ -234,32 +235,33 @@
 #define google_census_IntervalStats_Window_window_size_tag 1
 #define google_census_IntervalStats_Window_count_tag 2
 #define google_census_IntervalStats_Window_mean_tag 3
-#define google_census_Metric_name_tag            1
-#define google_census_Metric_description_tag     2
-#define google_census_Metric_unit_tag            3
-#define google_census_Metric_id_tag              4
+#define google_census_Metric_view_name_tag       1
+#define google_census_Metric_aggregation_tag     2
+#define google_census_Metric_start_tag           3
+#define google_census_Metric_end_tag             4
+#define google_census_Resource_name_tag          1
+#define google_census_Resource_description_tag   2
+#define google_census_Resource_unit_tag          3
 #define google_census_View_name_tag              1
 #define google_census_View_description_tag       2
-#define google_census_View_metric_id_tag         3
+#define google_census_View_resource_name_tag     3
 #define google_census_View_aggregation_tag       4
 #define google_census_View_tag_key_tag           5
-#define google_census_ViewAggregations_aggregation_tag 1
-#define google_census_ViewAggregations_start_tag 2
-#define google_census_ViewAggregations_end_tag   3
-#define google_census_Aggregation_distribution_tag 3
+#define google_census_Aggregation_count_tag      3
 
-#define google_census_Aggregation_interval_stats_tag 4
+#define google_census_Aggregation_distribution_tag 4
+
+#define google_census_Aggregation_interval_stats_tag 5
 #define google_census_Aggregation_name_tag       1
 #define google_census_Aggregation_description_tag 2
-#define google_census_Aggregation_tag_tag        5
+#define google_census_Aggregation_tag_tag        6
 
 /* Struct field encoding specification for nanopb */
 extern const pb_field_t google_census_Duration_fields[3];
 extern const pb_field_t google_census_Timestamp_fields[3];
-extern const pb_field_t google_census_Metric_fields[5];
-extern const pb_field_t google_census_Metric_BasicUnit_fields[2];
-extern const pb_field_t google_census_Metric_MeasurementUnit_fields[4];
-extern const pb_field_t google_census_AggregationDescriptor_fields[3];
+extern const pb_field_t google_census_Resource_fields[4];
+extern const pb_field_t google_census_Resource_MeasurementUnit_fields[4];
+extern const pb_field_t google_census_AggregationDescriptor_fields[4];
 extern const pb_field_t google_census_AggregationDescriptor_BucketBoundaries_fields[2];
 extern const pb_field_t google_census_AggregationDescriptor_IntervalBoundaries_fields[2];
 extern const pb_field_t google_census_Distribution_fields[5];
@@ -268,13 +270,12 @@
 extern const pb_field_t google_census_IntervalStats_Window_fields[4];
 extern const pb_field_t google_census_Tag_fields[3];
 extern const pb_field_t google_census_View_fields[6];
-extern const pb_field_t google_census_Aggregation_fields[6];
-extern const pb_field_t google_census_ViewAggregations_fields[4];
+extern const pb_field_t google_census_Aggregation_fields[7];
+extern const pb_field_t google_census_Metric_fields[5];
 
 /* Maximum encoded size of messages (where known) */
 #define google_census_Duration_size              22
 #define google_census_Timestamp_size             22
-#define google_census_Metric_BasicUnit_size      2
 #define google_census_Distribution_Range_size    18
 #define google_census_IntervalStats_Window_size  44
 #define google_census_Tag_size                   516
diff --git a/src/core/ext/census/gen/trace_context.pb.c b/src/core/ext/census/gen/trace_context.pb.c
new file mode 100644
index 0000000..c8aea32
--- /dev/null
+++ b/src/core/ext/census/gen/trace_context.pb.c
@@ -0,0 +1,81 @@
+/*
+ *
+ * Copyright 2016, 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.
+ *
+ */
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.5-dev */
+
+#include "src/core/ext/census/gen/trace_context.pb.h"
+
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t google_trace_TraceId_fields[3] = {
+    PB_FIELD(  1, FIXED64 , OPTIONAL, STATIC  , FIRST, google_trace_TraceId, hi, hi, 0),
+    PB_FIELD(  2, FIXED64 , OPTIONAL, STATIC  , OTHER, google_trace_TraceId, lo, hi, 0),
+    PB_LAST_FIELD
+};
+
+const pb_field_t google_trace_TraceContext_fields[4] = {
+    PB_FIELD(  1, MESSAGE , OPTIONAL, STATIC  , FIRST, google_trace_TraceContext, trace_id, trace_id, &google_trace_TraceId_fields),
+    PB_FIELD(  2, FIXED64 , OPTIONAL, STATIC  , OTHER, google_trace_TraceContext, span_id, trace_id, 0),
+    PB_FIELD(  3, BOOL    , OPTIONAL, STATIC  , OTHER, google_trace_TraceContext, is_sampled, span_id, 0),
+    PB_LAST_FIELD
+};
+
+
+/* Check that field information fits in pb_field_t */
+#if !defined(PB_FIELD_32BIT)
+/* If you get an error here, it means that you need to define PB_FIELD_32BIT
+ * compile-time option. You can do that in pb.h or on compiler command line.
+ * 
+ * The reason you need to do this is that some of your messages contain tag
+ * numbers or field sizes that are larger than what can fit in 8 or 16 bit
+ * field descriptors.
+ */
+PB_STATIC_ASSERT((pb_membersize(google_trace_TraceContext, trace_id) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_trace_TraceId_google_trace_TraceContext)
+#endif
+
+#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
+/* If you get an error here, it means that you need to define PB_FIELD_16BIT
+ * compile-time option. You can do that in pb.h or on compiler command line.
+ * 
+ * The reason you need to do this is that some of your messages contain tag
+ * numbers or field sizes that are larger than what can fit in the default
+ * 8 bit descriptors.
+ */
+PB_STATIC_ASSERT((pb_membersize(google_trace_TraceContext, trace_id) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_trace_TraceId_google_trace_TraceContext)
+#endif
+
+
diff --git a/src/core/ext/census/gen/trace_context.pb.h b/src/core/ext/census/gen/trace_context.pb.h
new file mode 100644
index 0000000..263c4c5
--- /dev/null
+++ b/src/core/ext/census/gen/trace_context.pb.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright 2016, 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.
+ *
+ */
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.5-dev */
+
+#ifndef GRPC_CORE_EXT_CENSUS_GEN_TRACE_CONTEXT_PB_H
+#define GRPC_CORE_EXT_CENSUS_GEN_TRACE_CONTEXT_PB_H
+#include "third_party/nanopb/pb.h"
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Struct definitions */
+typedef struct _google_trace_TraceId {
+    bool has_hi;
+    uint64_t hi;
+    bool has_lo;
+    uint64_t lo;
+} google_trace_TraceId;
+
+typedef struct _google_trace_TraceContext {
+    bool has_trace_id;
+    google_trace_TraceId trace_id;
+    bool has_span_id;
+    uint64_t span_id;
+    bool has_is_sampled;
+    bool is_sampled;
+} google_trace_TraceContext;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define google_trace_TraceId_init_default        {false, 0, false, 0}
+#define google_trace_TraceContext_init_default   {false, google_trace_TraceId_init_default, false, 0, false, 0}
+#define google_trace_TraceId_init_zero           {false, 0, false, 0}
+#define google_trace_TraceContext_init_zero      {false, google_trace_TraceId_init_zero, false, 0, false, 0}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define google_trace_TraceId_hi_tag              1
+#define google_trace_TraceId_lo_tag              2
+#define google_trace_TraceContext_trace_id_tag   1
+#define google_trace_TraceContext_span_id_tag    2
+#define google_trace_TraceContext_is_sampled_tag 3
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t google_trace_TraceId_fields[3];
+extern const pb_field_t google_trace_TraceContext_fields[4];
+
+/* Maximum encoded size of messages (where known) */
+#define google_trace_TraceId_size                18
+#define google_trace_TraceContext_size           31
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define TRACE_CONTEXT_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/src/core/ext/census/grpc_filter.c b/src/core/ext/census/grpc_filter.c
index f51d850..9dacc17 100644
--- a/src/core/ext/census/grpc_filter.c
+++ b/src/core/ext/census/grpc_filter.c
@@ -127,38 +127,40 @@
   grpc_call_next_op(exec_ctx, elem, op);
 }
 
-static void client_init_call_elem(grpc_exec_ctx *exec_ctx,
-                                  grpc_call_element *elem,
-                                  grpc_call_element_args *args) {
+static grpc_error *client_init_call_elem(grpc_exec_ctx *exec_ctx,
+                                         grpc_call_element *elem,
+                                         grpc_call_element_args *args) {
   call_data *d = elem->call_data;
   GPR_ASSERT(d != NULL);
   memset(d, 0, sizeof(*d));
   d->start_ts = gpr_now(GPR_CLOCK_REALTIME);
+  return GRPC_ERROR_NONE;
 }
 
 static void client_destroy_call_elem(grpc_exec_ctx *exec_ctx,
                                      grpc_call_element *elem,
-                                     const grpc_call_stats *stats,
+                                     const grpc_call_final_info *final_info,
                                      void *ignored) {
   call_data *d = elem->call_data;
   GPR_ASSERT(d != NULL);
   /* TODO(hongyu): record rpc client stats and census_rpc_end_op here */
 }
 
-static void server_init_call_elem(grpc_exec_ctx *exec_ctx,
-                                  grpc_call_element *elem,
-                                  grpc_call_element_args *args) {
+static grpc_error *server_init_call_elem(grpc_exec_ctx *exec_ctx,
+                                         grpc_call_element *elem,
+                                         grpc_call_element_args *args) {
   call_data *d = elem->call_data;
   GPR_ASSERT(d != NULL);
   memset(d, 0, sizeof(*d));
   d->start_ts = gpr_now(GPR_CLOCK_REALTIME);
   /* TODO(hongyu): call census_tracing_start_op here. */
   grpc_closure_init(&d->finish_recv, server_on_done_recv, elem);
+  return GRPC_ERROR_NONE;
 }
 
 static void server_destroy_call_elem(grpc_exec_ctx *exec_ctx,
                                      grpc_call_element *elem,
-                                     const grpc_call_stats *stats,
+                                     const grpc_call_final_info *final_info,
                                      void *ignored) {
   call_data *d = elem->call_data;
   GPR_ASSERT(d != NULL);
diff --git a/src/core/ext/census/initialize.c b/src/core/ext/census/initialize.c
index 896276e..55cbbe8 100644
--- a/src/core/ext/census/initialize.c
+++ b/src/core/ext/census/initialize.c
@@ -32,19 +32,31 @@
  */
 
 #include <grpc/census.h>
+#include "src/core/ext/census/base_resources.h"
+#include "src/core/ext/census/resource.h"
 
 static int features_enabled = CENSUS_FEATURE_NONE;
 
 int census_initialize(int features) {
   if (features_enabled != CENSUS_FEATURE_NONE) {
     // Must have been a previous call to census_initialize; return error
-    return 1;
+    return -1;
   }
-  features_enabled = features;
-  return 0;
+  features_enabled = features & CENSUS_FEATURE_ALL;
+  if (features & CENSUS_FEATURE_STATS) {
+    initialize_resources();
+    define_base_resources();
+  }
+
+  return features_enabled;
 }
 
-void census_shutdown(void) { features_enabled = CENSUS_FEATURE_NONE; }
+void census_shutdown(void) {
+  if (features_enabled & CENSUS_FEATURE_STATS) {
+    shutdown_resources();
+  }
+  features_enabled = CENSUS_FEATURE_NONE;
+}
 
 int census_supported(void) {
   /* TODO(aveitch): improve this as we implement features... */
diff --git a/src/core/ext/census/placeholders.c b/src/core/ext/census/placeholders.c
index fe23d13..9f99c5b 100644
--- a/src/core/ext/census/placeholders.c
+++ b/src/core/ext/census/placeholders.c
@@ -62,48 +62,3 @@
   (void)consume;
   abort();
 }
-
-const census_aggregation *census_view_aggregrations(const census_view *view) {
-  (void)view;
-  abort();
-}
-
-census_view *census_view_create(uint32_t metric_id, const census_context *tags,
-                                const census_aggregation *aggregations,
-                                size_t naggregations) {
-  (void)metric_id;
-  (void)tags;
-  (void)aggregations;
-  (void)naggregations;
-  abort();
-}
-
-const census_context *census_view_tags(const census_view *view) {
-  (void)view;
-  abort();
-}
-
-void census_view_delete(census_view *view) {
-  (void)view;
-  abort();
-}
-
-const census_view_data *census_view_get_data(const census_view *view) {
-  (void)view;
-  abort();
-}
-
-size_t census_view_metric(const census_view *view) {
-  (void)view;
-  abort();
-}
-
-size_t census_view_naggregations(const census_view *view) {
-  (void)view;
-  abort();
-}
-
-void census_view_reset(census_view *view) {
-  (void)view;
-  abort();
-}
diff --git a/src/core/ext/census/resource.c b/src/core/ext/census/resource.c
new file mode 100644
index 0000000..ed44f00
--- /dev/null
+++ b/src/core/ext/census/resource.c
@@ -0,0 +1,312 @@
+/*
+ *
+ * Copyright 2016, 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/ext/census/resource.h"
+#include "third_party/nanopb/pb_decode.h"
+
+#include <grpc/census.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include <stdbool.h>
+#include <string.h>
+
+// Protect local resource data structures.
+static gpr_mu resource_lock;
+
+// Deleteing and creating resources are relatively rare events, and should not
+// be done in the critical path of performance sensitive code. We record
+// current resource id's used in a simple array, and just search it each time
+// we need to assign a new id, or look up a resource.
+static resource **resources = NULL;
+
+// Number of entries in *resources
+static size_t n_resources = 0;
+
+// Number of defined resources
+static size_t n_defined_resources = 0;
+
+void initialize_resources(void) {
+  gpr_mu_init(&resource_lock);
+  gpr_mu_lock(&resource_lock);
+  GPR_ASSERT(resources == NULL && n_resources == 0 && n_defined_resources == 0);
+  gpr_mu_unlock(&resource_lock);
+}
+
+// Delete a resource given it's ID. The ID must be a valid resource ID. Must be
+// called with resource_lock held.
+static void delete_resource_locked(size_t rid) {
+  GPR_ASSERT(resources[rid] != NULL);
+  gpr_free(resources[rid]->name);
+  gpr_free(resources[rid]->description);
+  gpr_free(resources[rid]->numerators);
+  gpr_free(resources[rid]->denominators);
+  gpr_free(resources[rid]);
+  resources[rid] = NULL;
+  n_defined_resources--;
+}
+
+void shutdown_resources(void) {
+  gpr_mu_lock(&resource_lock);
+  for (size_t i = 0; i < n_resources; i++) {
+    if (resources[i] != NULL) {
+      delete_resource_locked(i);
+    }
+  }
+  GPR_ASSERT(n_defined_resources == 0);
+  gpr_free(resources);
+  resources = NULL;
+  n_resources = 0;
+  gpr_mu_unlock(&resource_lock);
+}
+
+// Check the contents of string fields in a resource proto.
+static bool validate_string(pb_istream_t *stream, const pb_field_t *field,
+                            void **arg) {
+  resource *vresource = (resource *)*arg;
+  switch (field->tag) {
+    case google_census_Resource_name_tag:
+      // Name must have at least one character
+      if (stream->bytes_left == 0) {
+        gpr_log(GPR_INFO, "Zero-length Resource name.");
+        return false;
+      }
+      vresource->name = gpr_malloc(stream->bytes_left + 1);
+      vresource->name[stream->bytes_left] = '\0';
+      if (!pb_read(stream, (uint8_t *)vresource->name, stream->bytes_left)) {
+        return false;
+      }
+      // Can't have same name as an existing resource.
+      for (size_t i = 0; i < n_resources; i++) {
+        resource *compare = resources[i];
+        if (compare == vresource || compare == NULL) continue;
+        if (strcmp(compare->name, vresource->name) == 0) {
+          gpr_log(GPR_INFO, "Duplicate Resource name %s.", vresource->name);
+          return false;
+        }
+      }
+      break;
+    case google_census_Resource_description_tag:
+      if (stream->bytes_left == 0) {
+        return true;
+      }
+      vresource->description = gpr_malloc(stream->bytes_left + 1);
+      vresource->description[stream->bytes_left] = '\0';
+      if (!pb_read(stream, (uint8_t *)vresource->description,
+                   stream->bytes_left)) {
+        return false;
+      }
+      break;
+    default:
+      // No other string fields in Resource. Print warning and skip.
+      gpr_log(GPR_INFO, "Unknown string field type in Resource protobuf.");
+      if (!pb_read(stream, NULL, stream->bytes_left)) {
+        return false;
+      }
+      break;
+  }
+  return true;
+}
+
+// Decode numerators/denominators in a stream. The `count` and `bup`
+// (BasicUnit pointer) are pointers to the approriate fields in a resource
+// struct.
+static bool validate_units_helper(pb_istream_t *stream, int *count,
+                                  google_census_Resource_BasicUnit **bup) {
+  while (stream->bytes_left) {
+    (*count)++;
+    // Have to allocate a new array of values. Normal case is 0 or 1, so
+    // this should normally not be an issue.
+    google_census_Resource_BasicUnit *new_bup =
+        gpr_malloc((size_t)*count * sizeof(google_census_Resource_BasicUnit));
+    if (*count != 1) {
+      memcpy(new_bup, *bup,
+             (size_t)(*count - 1) * sizeof(google_census_Resource_BasicUnit));
+      gpr_free(*bup);
+    }
+    *bup = new_bup;
+    uint64_t value;
+    if (!pb_decode_varint(stream, &value)) {
+      return false;
+    }
+    *(*bup + *count - 1) = (google_census_Resource_BasicUnit)value;
+  }
+  return true;
+}
+
+// Validate units field of a Resource proto.
+static bool validate_units(pb_istream_t *stream, const pb_field_t *field,
+                           void **arg) {
+  resource *vresource = (resource *)(*arg);
+  switch (field->tag) {
+    case google_census_Resource_MeasurementUnit_numerator_tag:
+      return validate_units_helper(stream, &vresource->n_numerators,
+                                   &vresource->numerators);
+      break;
+    case google_census_Resource_MeasurementUnit_denominator_tag:
+      return validate_units_helper(stream, &vresource->n_denominators,
+                                   &vresource->denominators);
+      break;
+    default:
+      gpr_log(GPR_ERROR, "Unknown field type.");
+      return false;
+      break;
+  }
+  return true;
+}
+
+// Validate the contents of a Resource proto. `id` is the intended resource id.
+static bool validate_resource_pb(const uint8_t *resource_pb,
+                                 size_t resource_pb_size, size_t id) {
+  GPR_ASSERT(id < n_resources);
+  if (resource_pb == NULL) {
+    return false;
+  }
+  google_census_Resource vresource;
+  vresource.name.funcs.decode = &validate_string;
+  vresource.name.arg = resources[id];
+  vresource.description.funcs.decode = &validate_string;
+  vresource.description.arg = resources[id];
+  vresource.unit.numerator.funcs.decode = &validate_units;
+  vresource.unit.numerator.arg = resources[id];
+  vresource.unit.denominator.funcs.decode = &validate_units;
+  vresource.unit.denominator.arg = resources[id];
+
+  pb_istream_t stream =
+      pb_istream_from_buffer((uint8_t *)resource_pb, resource_pb_size);
+  if (!pb_decode(&stream, google_census_Resource_fields, &vresource)) {
+    return false;
+  }
+  // A Resource must have a name, a unit, with at least one numerator.
+  return (resources[id]->name != NULL && vresource.has_unit &&
+          resources[id]->n_numerators > 0);
+}
+
+// Allocate a blank resource, and return associated ID. Must be called with
+// resource_lock held.
+size_t allocate_resource(void) {
+  // use next_id to optimize expected placement of next new resource.
+  static size_t next_id = 0;
+  size_t id = n_resources;  // resource ID - initialize to invalid value.
+  // Expand resources if needed.
+  if (n_resources == n_defined_resources) {
+    size_t new_n_resources = n_resources ? n_resources * 2 : 2;
+    resource **new_resources = gpr_malloc(new_n_resources * sizeof(resource *));
+    memcpy(new_resources, resources, n_resources * sizeof(resource *));
+    memset(new_resources + n_resources, 0,
+           (new_n_resources - n_resources) * sizeof(resource *));
+    gpr_free(resources);
+    resources = new_resources;
+    n_resources = new_n_resources;
+    id = n_defined_resources;
+  } else {
+    GPR_ASSERT(n_defined_resources < n_resources);
+    // Find a free id.
+    for (size_t base = 0; base < n_resources; base++) {
+      id = (next_id + base) % n_resources;
+      if (resources[id] == NULL) break;
+    }
+  }
+  GPR_ASSERT(id < n_resources && resources[id] == NULL);
+  resources[id] = gpr_malloc(sizeof(resource));
+  memset(resources[id], 0, sizeof(resource));
+  n_defined_resources++;
+  next_id = (id + 1) % n_resources;
+  return id;
+}
+
+int32_t census_define_resource(const uint8_t *resource_pb,
+                               size_t resource_pb_size) {
+  if (resource_pb == NULL) {
+    return -1;
+  }
+  gpr_mu_lock(&resource_lock);
+  size_t id = allocate_resource();
+  // Validate pb, extract name.
+  if (!validate_resource_pb(resource_pb, resource_pb_size, id)) {
+    delete_resource_locked(id);
+    gpr_mu_unlock(&resource_lock);
+    return -1;
+  }
+  gpr_mu_unlock(&resource_lock);
+  return (int32_t)id;
+}
+
+void census_delete_resource(int32_t rid) {
+  gpr_mu_lock(&resource_lock);
+  if (rid >= 0 && (size_t)rid < n_resources && resources[rid] != NULL) {
+    delete_resource_locked((size_t)rid);
+  }
+  gpr_mu_unlock(&resource_lock);
+}
+
+int32_t census_resource_id(const char *name) {
+  gpr_mu_lock(&resource_lock);
+  for (int32_t id = 0; (size_t)id < n_resources; id++) {
+    if (resources[id] != NULL && strcmp(resources[id]->name, name) == 0) {
+      gpr_mu_unlock(&resource_lock);
+      return id;
+    }
+  }
+  gpr_mu_unlock(&resource_lock);
+  return -1;
+}
+
+int32_t define_resource(const resource *base) {
+  GPR_ASSERT(base != NULL && base->name != NULL && base->n_numerators > 0 &&
+             base->numerators != NULL);
+  gpr_mu_lock(&resource_lock);
+  size_t id = allocate_resource();
+  size_t len = strlen(base->name) + 1;
+  resources[id]->name = gpr_malloc(len);
+  memcpy(resources[id]->name, base->name, len);
+  if (base->description) {
+    len = strlen(base->description) + 1;
+    resources[id]->description = gpr_malloc(len);
+    memcpy(resources[id]->description, base->description, len);
+  }
+  resources[id]->prefix = base->prefix;
+  resources[id]->n_numerators = base->n_numerators;
+  len = (size_t)base->n_numerators * sizeof(*base->numerators);
+  resources[id]->numerators = gpr_malloc(len);
+  memcpy(resources[id]->numerators, base->numerators, len);
+  resources[id]->n_denominators = base->n_denominators;
+  if (base->n_denominators != 0) {
+    len = (size_t)base->n_denominators * sizeof(*base->denominators);
+    resources[id]->denominators = gpr_malloc(len);
+    memcpy(resources[id]->denominators, base->denominators, len);
+  }
+  gpr_mu_unlock(&resource_lock);
+  return (int32_t)id;
+}
diff --git a/src/core/ext/census/resource.h b/src/core/ext/census/resource.h
new file mode 100644
index 0000000..591bff0
--- /dev/null
+++ b/src/core/ext/census/resource.h
@@ -0,0 +1,63 @@
+/*
+ *
+ * Copyright 2016, 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.
+ *
+ */
+
+/* Census-internal resource definition and manipluation functions. */
+#ifndef GRPC_CORE_EXT_CENSUS_RESOURCE_H
+#define GRPC_CORE_EXT_CENSUS_RESOURCE_H
+
+#include <grpc/grpc.h>
+#include "src/core/ext/census/gen/census.pb.h"
+
+/* Internal representation of a resource. */
+typedef struct {
+  char *name;
+  char *description;
+  int32_t prefix;
+  int n_numerators;
+  google_census_Resource_BasicUnit *numerators;
+  int n_denominators;
+  google_census_Resource_BasicUnit *denominators;
+} resource;
+
+/* Initialize and shutdown the resources subsystem. */
+void initialize_resources(void);
+void shutdown_resources(void);
+
+/* Add a new resource, given a proposed resource structure. Returns the
+   resource ID, or -ve on failure.
+   TODO(aveitch): this function exists to support addition of the base
+   resources. It should be removed when we have the ability to add resources
+   from configuration files. */
+int32_t define_resource(const resource *base);
+
+#endif /* GRPC_CORE_EXT_CENSUS_RESOURCE_H */
diff --git a/src/core/ext/census/trace_context.c b/src/core/ext/census/trace_context.c
new file mode 100644
index 0000000..fbb20d3
--- /dev/null
+++ b/src/core/ext/census/trace_context.c
@@ -0,0 +1,86 @@
+/*
+ *
+ * Copyright 2016, 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/ext/census/trace_context.h"
+
+#include <grpc/census.h>
+#include <grpc/support/log.h>
+#include <stdbool.h>
+
+#include "third_party/nanopb/pb_decode.h"
+#include "third_party/nanopb/pb_encode.h"
+
+// This function assumes the TraceContext is valid.
+size_t encode_trace_context(google_trace_TraceContext *ctxt, uint8_t *buffer,
+                            const size_t buf_size) {
+  // Create a stream that will write to our buffer.
+  pb_ostream_t stream = pb_ostream_from_buffer(buffer, buf_size);
+
+  // encode message
+  bool status = pb_encode(&stream, google_trace_TraceContext_fields, ctxt);
+
+  if (!status) {
+    gpr_log(GPR_DEBUG, "TraceContext encoding failed: %s",
+            PB_GET_ERROR(&stream));
+    return 0;
+  }
+
+  return stream.bytes_written;
+}
+
+bool decode_trace_context(google_trace_TraceContext *ctxt, uint8_t *buffer,
+                          const size_t nbytes) {
+  // Create a stream that reads nbytes from the buffer.
+  pb_istream_t stream = pb_istream_from_buffer(buffer, nbytes);
+
+  // decode message
+  bool status = pb_decode(&stream, google_trace_TraceContext_fields, ctxt);
+
+  if (!status) {
+    gpr_log(GPR_DEBUG, "TraceContext decoding failed: %s",
+            PB_GET_ERROR(&stream));
+    return false;
+  }
+
+  // check fields
+  if (!ctxt->has_trace_id) {
+    gpr_log(GPR_DEBUG, "Invalid TraceContext: missing trace_id");
+    return false;
+  }
+  if (!ctxt->has_span_id) {
+    gpr_log(GPR_DEBUG, "Invalid TraceContext: missing span_id");
+    return false;
+  }
+
+  return true;
+}
diff --git a/src/core/ext/census/trace_context.h b/src/core/ext/census/trace_context.h
new file mode 100644
index 0000000..ee71fef
--- /dev/null
+++ b/src/core/ext/census/trace_context.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2016, 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.
+ *
+ */
+
+/* Functions for manipulating trace contexts as defined in
+   src/proto/census/trace.proto */
+#ifndef GRPC_CORE_EXT_CENSUS_TRACE_CONTEXT_H
+#define GRPC_CORE_EXT_CENSUS_TRACE_CONTEXT_H
+
+#include "src/core/ext/census/gen/trace_context.pb.h"
+
+/* Maximum number of bytes required to encode a TraceContext (31)
+1 byte for trace_id field
+1 byte for trace_id length
+1 byte for trace_id.hi field
+8 bytes for trace_id.hi (uint64_t)
+1 byte for trace_id.lo field
+8 bytes for trace_id.lo (uint64_t)
+1 byte for span_id field
+8 bytes for span_id (uint64_t)
+1 byte for is_sampled field
+1 byte for is_sampled (bool) */
+#define TRACE_MAX_CONTEXT_SIZE 31
+
+/* Encode a trace context (ctxt) into proto format to the buffer provided.  The
+size of buffer must be at least TRACE_MAX_CONTEXT_SIZE.  On success, returns the
+number of bytes successfully encoded into buffer.  On failure, returns 0. */
+size_t encode_trace_context(google_trace_TraceContext *ctxt, uint8_t *buffer,
+                            const size_t buf_size);
+
+/* Decode a proto-encoded TraceContext from the provided buffer into the
+TraceContext structure (ctxt).  The function expects to be supplied the number
+of bytes to be read from buffer (nbytes).  This function will also validate that
+the TraceContext has a span_id and a trace_id, and will return false if either
+of these do not exist. On success, returns true and false otherwise. */
+bool decode_trace_context(google_trace_TraceContext *ctxt, uint8_t *buffer,
+                          const size_t nbytes);
+
+#endif
diff --git a/src/core/ext/census/tracing.c b/src/core/ext/census/tracing.c
index 3b5d6da..8f0e122 100644
--- a/src/core/ext/census/tracing.c
+++ b/src/core/ext/census/tracing.c
@@ -31,15 +31,19 @@
  *
  */
 
+//#include "src/core/ext/census/tracing.h"
+
 #include <grpc/census.h>
 
 /* TODO(aveitch): These are all placeholder implementations. */
 
-int census_trace_mask(const census_context *context) {
-  return CENSUS_TRACE_MASK_NONE;
-}
+// int census_trace_mask(const census_context *context) {
+//   return CENSUS_TRACE_MASK_NONE;
+// }
 
-void census_set_trace_mask(int trace_mask) {}
+// void census_set_trace_mask(int trace_mask) {}
 
-void census_trace_print(census_context *context, uint32_t type,
-                        const char *buffer, size_t n) {}
+// void census_trace_print(census_context *context, uint32_t type,
+//                         const char *buffer, size_t n) {}
+
+// void SetTracerParams(const Params& params);
diff --git a/src/core/ext/client_config/README.md b/src/core/ext/client_config/README.md
index 7024fd5..eda01e3 100644
--- a/src/core/ext/client_config/README.md
+++ b/src/core/ext/client_config/README.md
@@ -12,7 +12,7 @@
 - a load balancing policy to decide which server to send a request to
 - a set of filters to mutate outgoing requests (say, by adding metadata)
 
-The resolver provides this data as a stream of grpc_client_config objects to
+The resolver provides this data as a stream of grpc_resolver_result objects to
 the channel. We represent configuration as a stream so that it can be changed
 by the resolver during execution, by reacting to external events (such as a
 new configuration file being pushed to some store).
@@ -22,7 +22,7 @@
 --------------
 
 Load balancing configuration is provided by a grpc_lb_policy object, stored as
-part of grpc_client_config.
+part of grpc_resolver_result.
 
 The primary job of the load balancing policies is to pick a target server given only the
 initial metadata for a request. It does this by providing a grpc_subchannel
diff --git a/src/core/ext/client_config/client_channel.c b/src/core/ext/client_config/client_channel.c
index a096435..51f6f8e 100644
--- a/src/core/ext/client_config/client_channel.c
+++ b/src/core/ext/client_config/client_channel.c
@@ -33,6 +33,7 @@
 
 #include "src/core/ext/client_config/client_channel.h"
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -41,10 +42,13 @@
 #include <grpc/support/sync.h>
 #include <grpc/support/useful.h>
 
-#include "src/core/ext/client_config/subchannel_call_holder.h"
+#include "src/core/ext/client_config/lb_policy_registry.h"
+#include "src/core/ext/client_config/subchannel.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/channel/deadline_filter.h"
 #include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/iomgr/polling_entity.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/channel.h"
@@ -52,30 +56,33 @@
 
 /* Client channel implementation */
 
-typedef grpc_subchannel_call_holder call_data;
+/*************************************************************************
+ * CHANNEL-WIDE FUNCTIONS
+ */
 
 typedef struct client_channel_channel_data {
   /** resolver for this channel */
   grpc_resolver *resolver;
   /** have we started resolving this channel */
-  int started_resolving;
+  bool started_resolving;
+  /** client channel factory */
+  grpc_client_channel_factory *client_channel_factory;
 
   /** mutex protecting client configuration, including all
       variables below in this data structure */
-  gpr_mu mu_config;
-  /** currently active load balancer - guarded by mu_config */
+  gpr_mu mu;
+  /** currently active load balancer - guarded by mu */
   grpc_lb_policy *lb_policy;
-  /** incoming configuration - set by resolver.next
-      guarded by mu_config */
-  grpc_client_config *incoming_configuration;
+  /** incoming resolver result - set by resolver.next(), guarded by mu */
+  grpc_resolver_result *resolver_result;
   /** a list of closures that are all waiting for config to come in */
   grpc_closure_list waiting_for_config_closures;
   /** resolver callback */
-  grpc_closure on_config_changed;
+  grpc_closure on_resolver_result_changed;
   /** connectivity state being tracked */
   grpc_connectivity_state_tracker state_tracker;
   /** when an lb_policy arrives, should we try to exit idle */
-  int exit_idle_when_lb_policy_arrives;
+  bool exit_idle_when_lb_policy_arrives;
   /** owning stack */
   grpc_channel_stack *owning_stack;
   /** interested parties (owned) */
@@ -83,10 +90,8 @@
 } channel_data;
 
 /** We create one watcher for each new lb_policy that is returned from a
-   resolver,
-    to watch for state changes from the lb_policy. When a state change is seen,
-   we
-    update the channel, and create a new watcher */
+    resolver, to watch for state changes from the lb_policy. When a state
+    change is seen, we update the channel, and create a new watcher. */
 typedef struct {
   channel_data *chand;
   grpc_closure on_changed;
@@ -94,22 +99,6 @@
   grpc_lb_policy *lb_policy;
 } lb_policy_connectivity_watcher;
 
-typedef struct {
-  grpc_closure closure;
-  grpc_call_element *elem;
-} waiting_call;
-
-static char *cc_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {
-  return grpc_subchannel_call_holder_get_peer(exec_ctx, elem->call_data);
-}
-
-static void cc_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
-                                         grpc_call_element *elem,
-                                         grpc_transport_stream_op *op) {
-  GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
-  grpc_subchannel_call_holder_perform_op(exec_ctx, elem->call_data, op);
-}
-
 static void watch_lb_policy(grpc_exec_ctx *exec_ctx, channel_data *chand,
                             grpc_lb_policy *lb_policy,
                             grpc_connectivity_state current_state);
@@ -126,7 +115,7 @@
     grpc_lb_policy_cancel_picks(
         exec_ctx, chand->lb_policy,
         /* mask= */ GRPC_INITIAL_METADATA_IGNORE_CONNECTIVITY,
-        /* check= */ 0);
+        /* check= */ 0, GRPC_ERROR_REF(error));
   }
   grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, state, error,
                               reason);
@@ -156,9 +145,9 @@
                                        grpc_error *error) {
   lb_policy_connectivity_watcher *w = arg;
 
-  gpr_mu_lock(&w->chand->mu_config);
+  gpr_mu_lock(&w->chand->mu);
   on_lb_policy_state_changed_locked(exec_ctx, w, error);
-  gpr_mu_unlock(&w->chand->mu_config);
+  gpr_mu_unlock(&w->chand->mu);
 
   GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, "watch_lb_policy");
   gpr_free(w);
@@ -178,36 +167,69 @@
                                         &w->on_changed);
 }
 
-static void cc_on_config_changed(grpc_exec_ctx *exec_ctx, void *arg,
-                                 grpc_error *error) {
+static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg,
+                                       grpc_error *error) {
   channel_data *chand = arg;
   grpc_lb_policy *lb_policy = NULL;
   grpc_lb_policy *old_lb_policy;
   grpc_connectivity_state state = GRPC_CHANNEL_TRANSIENT_FAILURE;
-  int exit_idle = 0;
+  bool exit_idle = false;
   grpc_error *state_error = GRPC_ERROR_CREATE("No load balancing policy");
 
-  if (chand->incoming_configuration != NULL) {
-    lb_policy = grpc_client_config_get_lb_policy(chand->incoming_configuration);
+  if (chand->resolver_result != NULL) {
+    grpc_lb_policy_args lb_policy_args;
+    lb_policy_args.server_name =
+        grpc_resolver_result_get_server_name(chand->resolver_result);
+    lb_policy_args.addresses =
+        grpc_resolver_result_get_addresses(chand->resolver_result);
+    lb_policy_args.additional_args =
+        grpc_resolver_result_get_lb_policy_args(chand->resolver_result);
+    lb_policy_args.client_channel_factory = chand->client_channel_factory;
+
+    // Special case: If all of the addresses are balancer addresses,
+    // assume that we should use the grpclb policy, regardless of what the
+    // resolver actually specified.
+    const char *lb_policy_name =
+        grpc_resolver_result_get_lb_policy_name(chand->resolver_result);
+    bool found_backend_address = false;
+    for (size_t i = 0; i < lb_policy_args.addresses->num_addresses; ++i) {
+      if (!lb_policy_args.addresses->addresses[i].is_balancer) {
+        found_backend_address = true;
+        break;
+      }
+    }
+    if (!found_backend_address) {
+      if (lb_policy_name != NULL && strcmp(lb_policy_name, "grpclb") != 0) {
+        gpr_log(GPR_INFO,
+                "resolver requested LB policy %s but provided only balancer "
+                "addresses, no backend addresses -- forcing use of grpclb LB "
+                "policy",
+                (lb_policy_name == NULL ? "(none)" : lb_policy_name));
+      }
+      lb_policy_name = "grpclb";
+    }
+    // Use pick_first if nothing was specified and we didn't select grpclb
+    // above.
+    if (lb_policy_name == NULL) lb_policy_name = "pick_first";
+
+    lb_policy =
+        grpc_lb_policy_create(exec_ctx, lb_policy_name, &lb_policy_args);
     if (lb_policy != NULL) {
-      GRPC_LB_POLICY_REF(lb_policy, "channel");
       GRPC_LB_POLICY_REF(lb_policy, "config_change");
       GRPC_ERROR_UNREF(state_error);
       state =
           grpc_lb_policy_check_connectivity(exec_ctx, lb_policy, &state_error);
     }
-
-    grpc_client_config_unref(exec_ctx, chand->incoming_configuration);
+    grpc_resolver_result_unref(exec_ctx, chand->resolver_result);
+    chand->resolver_result = NULL;
   }
 
-  chand->incoming_configuration = NULL;
-
   if (lb_policy != NULL) {
     grpc_pollset_set_add_pollset_set(exec_ctx, lb_policy->interested_parties,
                                      chand->interested_parties);
   }
 
-  gpr_mu_lock(&chand->mu_config);
+  gpr_mu_lock(&chand->mu);
   old_lb_policy = chand->lb_policy;
   chand->lb_policy = lb_policy;
   if (lb_policy != NULL) {
@@ -222,8 +244,8 @@
   }
   if (lb_policy != NULL && chand->exit_idle_when_lb_policy_arrives) {
     GRPC_LB_POLICY_REF(lb_policy, "exit_idle");
-    exit_idle = 1;
-    chand->exit_idle_when_lb_policy_arrives = 0;
+    exit_idle = true;
+    chand->exit_idle_when_lb_policy_arrives = false;
   }
 
   if (error == GRPC_ERROR_NONE && chand->resolver) {
@@ -233,10 +255,9 @@
       watch_lb_policy(exec_ctx, chand, lb_policy, state);
     }
     GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver");
-    grpc_resolver_next(exec_ctx, chand->resolver,
-                       &chand->incoming_configuration,
-                       &chand->on_config_changed);
-    gpr_mu_unlock(&chand->mu_config);
+    grpc_resolver_next(exec_ctx, chand->resolver, &chand->resolver_result,
+                       &chand->on_resolver_result_changed);
+    gpr_mu_unlock(&chand->mu);
   } else {
     if (chand->resolver != NULL) {
       grpc_resolver_shutdown(exec_ctx, chand->resolver);
@@ -249,7 +270,7 @@
         GRPC_ERROR_CREATE_REFERENCING("Got config after disconnection", refs,
                                       GPR_ARRAY_SIZE(refs)),
         "resolver_gone");
-    gpr_mu_unlock(&chand->mu_config);
+    gpr_mu_unlock(&chand->mu);
   }
 
   if (exit_idle) {
@@ -284,7 +305,7 @@
                                  op->bind_pollset);
   }
 
-  gpr_mu_lock(&chand->mu_config);
+  gpr_mu_lock(&chand->mu);
   if (op->on_connectivity_state_change != NULL) {
     grpc_connectivity_state_notify_on_state_change(
         exec_ctx, &chand->state_tracker, op->connectivity_state,
@@ -329,7 +350,209 @@
     }
     GRPC_ERROR_UNREF(op->disconnect_with_error);
   }
-  gpr_mu_unlock(&chand->mu_config);
+  gpr_mu_unlock(&chand->mu);
+}
+
+/* Constructor for channel_data */
+static void cc_init_channel_elem(grpc_exec_ctx *exec_ctx,
+                                 grpc_channel_element *elem,
+                                 grpc_channel_element_args *args) {
+  channel_data *chand = elem->channel_data;
+
+  memset(chand, 0, sizeof(*chand));
+
+  GPR_ASSERT(args->is_last);
+  GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
+
+  gpr_mu_init(&chand->mu);
+  grpc_closure_init(&chand->on_resolver_result_changed,
+                    on_resolver_result_changed, chand);
+  chand->owning_stack = args->channel_stack;
+
+  grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE,
+                               "client_channel");
+  chand->interested_parties = grpc_pollset_set_create();
+}
+
+/* Destructor for channel_data */
+static void cc_destroy_channel_elem(grpc_exec_ctx *exec_ctx,
+                                    grpc_channel_element *elem) {
+  channel_data *chand = elem->channel_data;
+
+  if (chand->resolver != NULL) {
+    grpc_resolver_shutdown(exec_ctx, chand->resolver);
+    GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel");
+  }
+  if (chand->client_channel_factory != NULL) {
+    grpc_client_channel_factory_unref(exec_ctx, chand->client_channel_factory);
+  }
+  if (chand->lb_policy != NULL) {
+    grpc_pollset_set_del_pollset_set(exec_ctx,
+                                     chand->lb_policy->interested_parties,
+                                     chand->interested_parties);
+    GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel");
+  }
+  grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker);
+  grpc_pollset_set_destroy(chand->interested_parties);
+  gpr_mu_destroy(&chand->mu);
+}
+
+/*************************************************************************
+ * PER-CALL FUNCTIONS
+ */
+
+#define GET_CALL(call_data) \
+  ((grpc_subchannel_call *)(gpr_atm_acq_load(&(call_data)->subchannel_call)))
+
+#define CANCELLED_CALL ((grpc_subchannel_call *)1)
+
+typedef enum {
+  GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING,
+  GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL
+} subchannel_creation_phase;
+
+/** Call data.  Holds a pointer to grpc_subchannel_call and the
+    associated machinery to create such a pointer.
+    Handles queueing of stream ops until a call object is ready, waiting
+    for initial metadata before trying to create a call object,
+    and handling cancellation gracefully. */
+typedef struct client_channel_call_data {
+  // State for handling deadlines.
+  // The code in deadline_filter.c requires this to be the first field.
+  // TODO(roth): This is slightly sub-optimal in that grpc_deadline_state
+  // and this struct both independently store a pointer to the call
+  // stack and each has its own mutex.  If/when we have time, find a way
+  // to avoid this without breaking the grpc_deadline_state abstraction.
+  grpc_deadline_state deadline_state;
+  gpr_timespec deadline;
+
+  grpc_error *cancel_error;
+
+  /** either 0 for no call, 1 for cancelled, or a pointer to a
+      grpc_subchannel_call */
+  gpr_atm subchannel_call;
+
+  gpr_mu mu;
+
+  subchannel_creation_phase creation_phase;
+  grpc_connected_subchannel *connected_subchannel;
+  grpc_polling_entity *pollent;
+
+  grpc_transport_stream_op **waiting_ops;
+  size_t waiting_ops_count;
+  size_t waiting_ops_capacity;
+
+  grpc_closure next_step;
+
+  grpc_call_stack *owning_call;
+
+  grpc_linked_mdelem lb_token_mdelem;
+} call_data;
+
+static void add_waiting_locked(call_data *calld, grpc_transport_stream_op *op) {
+  GPR_TIMER_BEGIN("add_waiting_locked", 0);
+  if (calld->waiting_ops_count == calld->waiting_ops_capacity) {
+    calld->waiting_ops_capacity = GPR_MAX(3, 2 * calld->waiting_ops_capacity);
+    calld->waiting_ops =
+        gpr_realloc(calld->waiting_ops,
+                    calld->waiting_ops_capacity * sizeof(*calld->waiting_ops));
+  }
+  calld->waiting_ops[calld->waiting_ops_count++] = op;
+  GPR_TIMER_END("add_waiting_locked", 0);
+}
+
+static void fail_locked(grpc_exec_ctx *exec_ctx, call_data *calld,
+                        grpc_error *error) {
+  size_t i;
+  for (i = 0; i < calld->waiting_ops_count; i++) {
+    grpc_transport_stream_op_finish_with_failure(
+        exec_ctx, calld->waiting_ops[i], GRPC_ERROR_REF(error));
+  }
+  calld->waiting_ops_count = 0;
+  GRPC_ERROR_UNREF(error);
+}
+
+typedef struct {
+  grpc_transport_stream_op **ops;
+  size_t nops;
+  grpc_subchannel_call *call;
+} retry_ops_args;
+
+static void retry_ops(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) {
+  retry_ops_args *a = args;
+  size_t i;
+  for (i = 0; i < a->nops; i++) {
+    grpc_subchannel_call_process_op(exec_ctx, a->call, a->ops[i]);
+  }
+  GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, a->call, "retry_ops");
+  gpr_free(a->ops);
+  gpr_free(a);
+}
+
+static void retry_waiting_locked(grpc_exec_ctx *exec_ctx, call_data *calld) {
+  if (calld->waiting_ops_count == 0) {
+    return;
+  }
+
+  retry_ops_args *a = gpr_malloc(sizeof(*a));
+  a->ops = calld->waiting_ops;
+  a->nops = calld->waiting_ops_count;
+  a->call = GET_CALL(calld);
+  if (a->call == CANCELLED_CALL) {
+    gpr_free(a);
+    fail_locked(exec_ctx, calld, GRPC_ERROR_CANCELLED);
+    return;
+  }
+  calld->waiting_ops = NULL;
+  calld->waiting_ops_count = 0;
+  calld->waiting_ops_capacity = 0;
+  GRPC_SUBCHANNEL_CALL_REF(a->call, "retry_ops");
+  grpc_exec_ctx_sched(exec_ctx, grpc_closure_create(retry_ops, a),
+                      GRPC_ERROR_NONE, NULL);
+}
+
+static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg,
+                             grpc_error *error) {
+  call_data *calld = arg;
+  gpr_mu_lock(&calld->mu);
+  GPR_ASSERT(calld->creation_phase ==
+             GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL);
+  calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
+  if (calld->connected_subchannel == NULL) {
+    gpr_atm_no_barrier_store(&calld->subchannel_call, 1);
+    fail_locked(exec_ctx, calld, GRPC_ERROR_CREATE_REFERENCING(
+                                     "Failed to create subchannel", &error, 1));
+  } else if (GET_CALL(calld) == CANCELLED_CALL) {
+    /* already cancelled before subchannel became ready */
+    fail_locked(exec_ctx, calld,
+                GRPC_ERROR_CREATE_REFERENCING(
+                    "Cancelled before creating subchannel", &error, 1));
+  } else {
+    grpc_subchannel_call *subchannel_call = NULL;
+    grpc_error *new_error = grpc_connected_subchannel_create_call(
+        exec_ctx, calld->connected_subchannel, calld->pollent, calld->deadline,
+        &subchannel_call);
+    if (new_error != GRPC_ERROR_NONE) {
+      new_error = grpc_error_add_child(new_error, error);
+      subchannel_call = CANCELLED_CALL;
+      fail_locked(exec_ctx, calld, new_error);
+    }
+    gpr_atm_rel_store(&calld->subchannel_call,
+                      (gpr_atm)(uintptr_t)subchannel_call);
+    retry_waiting_locked(exec_ctx, calld);
+  }
+  gpr_mu_unlock(&calld->mu);
+  GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_subchannel");
+}
+
+static char *cc_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {
+  call_data *calld = elem->call_data;
+  grpc_subchannel_call *subchannel_call = GET_CALL(calld);
+  if (subchannel_call == NULL || subchannel_call == CANCELLED_CALL) {
+    return NULL;
+  } else {
+    return grpc_subchannel_call_get_peer(exec_ctx, subchannel_call);
+  }
 }
 
 typedef struct {
@@ -341,11 +564,11 @@
   grpc_closure closure;
 } continue_picking_args;
 
-static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *arg,
-                              grpc_metadata_batch *initial_metadata,
-                              uint32_t initial_metadata_flags,
-                              grpc_connected_subchannel **connected_subchannel,
-                              grpc_closure *on_ready);
+static bool pick_subchannel(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+                            grpc_metadata_batch *initial_metadata,
+                            uint32_t initial_metadata_flags,
+                            grpc_connected_subchannel **connected_subchannel,
+                            grpc_closure *on_ready, grpc_error *error);
 
 static void continue_picking(grpc_exec_ctx *exec_ctx, void *arg,
                              grpc_error *error) {
@@ -354,22 +577,22 @@
     /* cancelled, do nothing */
   } else if (error != GRPC_ERROR_NONE) {
     grpc_exec_ctx_sched(exec_ctx, cpa->on_ready, GRPC_ERROR_REF(error), NULL);
-  } else if (cc_pick_subchannel(exec_ctx, cpa->elem, cpa->initial_metadata,
-                                cpa->initial_metadata_flags,
-                                cpa->connected_subchannel, cpa->on_ready)) {
+  } else if (pick_subchannel(exec_ctx, cpa->elem, cpa->initial_metadata,
+                             cpa->initial_metadata_flags,
+                             cpa->connected_subchannel, cpa->on_ready,
+                             GRPC_ERROR_NONE)) {
     grpc_exec_ctx_sched(exec_ctx, cpa->on_ready, GRPC_ERROR_NONE, NULL);
   }
   gpr_free(cpa);
 }
 
-static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp,
-                              grpc_metadata_batch *initial_metadata,
-                              uint32_t initial_metadata_flags,
-                              grpc_connected_subchannel **connected_subchannel,
-                              grpc_closure *on_ready) {
-  GPR_TIMER_BEGIN("cc_pick_subchannel", 0);
+static bool pick_subchannel(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+                            grpc_metadata_batch *initial_metadata,
+                            uint32_t initial_metadata_flags,
+                            grpc_connected_subchannel **connected_subchannel,
+                            grpc_closure *on_ready, grpc_error *error) {
+  GPR_TIMER_BEGIN("pick_subchannel", 0);
 
-  grpc_call_element *elem = elemp;
   channel_data *chand = elem->channel_data;
   call_data *calld = elem->call_data;
   continue_picking_args *cpa;
@@ -377,43 +600,48 @@
 
   GPR_ASSERT(connected_subchannel);
 
-  gpr_mu_lock(&chand->mu_config);
+  gpr_mu_lock(&chand->mu);
   if (initial_metadata == NULL) {
     if (chand->lb_policy != NULL) {
       grpc_lb_policy_cancel_pick(exec_ctx, chand->lb_policy,
-                                 connected_subchannel);
+                                 connected_subchannel, GRPC_ERROR_REF(error));
     }
     for (closure = chand->waiting_for_config_closures.head; closure != NULL;
          closure = closure->next_data.next) {
       cpa = closure->cb_arg;
       if (cpa->connected_subchannel == connected_subchannel) {
         cpa->connected_subchannel = NULL;
-        grpc_exec_ctx_sched(exec_ctx, cpa->on_ready,
-                            GRPC_ERROR_CREATE("Pick cancelled"), NULL);
+        grpc_exec_ctx_sched(
+            exec_ctx, cpa->on_ready,
+            GRPC_ERROR_CREATE_REFERENCING("Pick cancelled", &error, 1), NULL);
       }
     }
-    gpr_mu_unlock(&chand->mu_config);
-    GPR_TIMER_END("cc_pick_subchannel", 0);
-    return 1;
+    gpr_mu_unlock(&chand->mu);
+    GPR_TIMER_END("pick_subchannel", 0);
+    GRPC_ERROR_UNREF(error);
+    return true;
   }
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   if (chand->lb_policy != NULL) {
     grpc_lb_policy *lb_policy = chand->lb_policy;
     int r;
-    GRPC_LB_POLICY_REF(lb_policy, "cc_pick_subchannel");
-    gpr_mu_unlock(&chand->mu_config);
-    r = grpc_lb_policy_pick(exec_ctx, lb_policy, calld->pollent,
-                            initial_metadata, initial_metadata_flags,
-                            connected_subchannel, on_ready);
-    GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "cc_pick_subchannel");
-    GPR_TIMER_END("cc_pick_subchannel", 0);
+    GRPC_LB_POLICY_REF(lb_policy, "pick_subchannel");
+    gpr_mu_unlock(&chand->mu);
+    // TODO(dgq): make this deadline configurable somehow.
+    const grpc_lb_policy_pick_args inputs = {
+        calld->pollent, initial_metadata, initial_metadata_flags,
+        &calld->lb_token_mdelem, gpr_inf_future(GPR_CLOCK_MONOTONIC)};
+    r = grpc_lb_policy_pick(exec_ctx, lb_policy, &inputs, connected_subchannel,
+                            NULL, on_ready);
+    GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "pick_subchannel");
+    GPR_TIMER_END("pick_subchannel", 0);
     return r;
   }
   if (chand->resolver != NULL && !chand->started_resolving) {
-    chand->started_resolving = 1;
+    chand->started_resolving = true;
     GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver");
-    grpc_resolver_next(exec_ctx, chand->resolver,
-                       &chand->incoming_configuration,
-                       &chand->on_config_changed);
+    grpc_resolver_next(exec_ctx, chand->resolver, &chand->resolver_result,
+                       &chand->on_resolver_result_changed);
   }
   if (chand->resolver != NULL) {
     cpa = gpr_malloc(sizeof(*cpa));
@@ -429,67 +657,160 @@
     grpc_exec_ctx_sched(exec_ctx, on_ready, GRPC_ERROR_CREATE("Disconnected"),
                         NULL);
   }
-  gpr_mu_unlock(&chand->mu_config);
+  gpr_mu_unlock(&chand->mu);
 
-  GPR_TIMER_END("cc_pick_subchannel", 0);
-  return 0;
+  GPR_TIMER_END("pick_subchannel", 0);
+  return false;
+}
+
+// The logic here is fairly complicated, due to (a) the fact that we
+// need to handle the case where we receive the send op before the
+// initial metadata op, and (b) the need for efficiency, especially in
+// the streaming case.
+// TODO(ctiller): Explain this more thoroughly.
+static void cc_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
+                                         grpc_call_element *elem,
+                                         grpc_transport_stream_op *op) {
+  call_data *calld = elem->call_data;
+  GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
+  grpc_deadline_state_client_start_transport_stream_op(exec_ctx, elem, op);
+  /* try to (atomically) get the call */
+  grpc_subchannel_call *call = GET_CALL(calld);
+  GPR_TIMER_BEGIN("cc_start_transport_stream_op", 0);
+  if (call == CANCELLED_CALL) {
+    grpc_transport_stream_op_finish_with_failure(
+        exec_ctx, op, GRPC_ERROR_REF(calld->cancel_error));
+    GPR_TIMER_END("cc_start_transport_stream_op", 0);
+    return;
+  }
+  if (call != NULL) {
+    grpc_subchannel_call_process_op(exec_ctx, call, op);
+    GPR_TIMER_END("cc_start_transport_stream_op", 0);
+    return;
+  }
+  /* we failed; lock and figure out what to do */
+  gpr_mu_lock(&calld->mu);
+retry:
+  /* need to recheck that another thread hasn't set the call */
+  call = GET_CALL(calld);
+  if (call == CANCELLED_CALL) {
+    gpr_mu_unlock(&calld->mu);
+    grpc_transport_stream_op_finish_with_failure(
+        exec_ctx, op, GRPC_ERROR_REF(calld->cancel_error));
+    GPR_TIMER_END("cc_start_transport_stream_op", 0);
+    return;
+  }
+  if (call != NULL) {
+    gpr_mu_unlock(&calld->mu);
+    grpc_subchannel_call_process_op(exec_ctx, call, op);
+    GPR_TIMER_END("cc_start_transport_stream_op", 0);
+    return;
+  }
+  /* if this is a cancellation, then we can raise our cancelled flag */
+  if (op->cancel_error != GRPC_ERROR_NONE) {
+    if (!gpr_atm_rel_cas(&calld->subchannel_call, 0,
+                         (gpr_atm)(uintptr_t)CANCELLED_CALL)) {
+      goto retry;
+    } else {
+      // Stash a copy of cancel_error in our call data, so that we can use
+      // it for subsequent operations.  This ensures that if the call is
+      // cancelled before any ops are passed down (e.g., if the deadline
+      // is in the past when the call starts), we can return the right
+      // error to the caller when the first op does get passed down.
+      calld->cancel_error = GRPC_ERROR_REF(op->cancel_error);
+      switch (calld->creation_phase) {
+        case GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING:
+          fail_locked(exec_ctx, calld, GRPC_ERROR_REF(op->cancel_error));
+          break;
+        case GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL:
+          pick_subchannel(exec_ctx, elem, NULL, 0, &calld->connected_subchannel,
+                          NULL, GRPC_ERROR_REF(op->cancel_error));
+          break;
+      }
+      gpr_mu_unlock(&calld->mu);
+      grpc_transport_stream_op_finish_with_failure(
+          exec_ctx, op, GRPC_ERROR_REF(op->cancel_error));
+      GPR_TIMER_END("cc_start_transport_stream_op", 0);
+      return;
+    }
+  }
+  /* if we don't have a subchannel, try to get one */
+  if (calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING &&
+      calld->connected_subchannel == NULL &&
+      op->send_initial_metadata != NULL) {
+    calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL;
+    grpc_closure_init(&calld->next_step, subchannel_ready, calld);
+    GRPC_CALL_STACK_REF(calld->owning_call, "pick_subchannel");
+    if (pick_subchannel(exec_ctx, elem, op->send_initial_metadata,
+                        op->send_initial_metadata_flags,
+                        &calld->connected_subchannel, &calld->next_step,
+                        GRPC_ERROR_NONE)) {
+      calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
+      GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_subchannel");
+    }
+  }
+  /* if we've got a subchannel, then let's ask it to create a call */
+  if (calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING &&
+      calld->connected_subchannel != NULL) {
+    grpc_subchannel_call *subchannel_call = NULL;
+    grpc_error *error = grpc_connected_subchannel_create_call(
+        exec_ctx, calld->connected_subchannel, calld->pollent, calld->deadline,
+        &subchannel_call);
+    if (error != GRPC_ERROR_NONE) {
+      subchannel_call = CANCELLED_CALL;
+      fail_locked(exec_ctx, calld, GRPC_ERROR_REF(error));
+      grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error);
+    }
+    gpr_atm_rel_store(&calld->subchannel_call,
+                      (gpr_atm)(uintptr_t)subchannel_call);
+    retry_waiting_locked(exec_ctx, calld);
+    goto retry;
+  }
+  /* nothing to be done but wait */
+  add_waiting_locked(calld, op);
+  gpr_mu_unlock(&calld->mu);
+  GPR_TIMER_END("cc_start_transport_stream_op", 0);
 }
 
 /* Constructor for call_data */
-static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {
-  grpc_subchannel_call_holder_init(elem->call_data, cc_pick_subchannel, elem,
-                                   args->call_stack);
+static grpc_error *cc_init_call_elem(grpc_exec_ctx *exec_ctx,
+                                     grpc_call_element *elem,
+                                     grpc_call_element_args *args) {
+  call_data *calld = elem->call_data;
+  grpc_deadline_state_init(exec_ctx, elem, args);
+  calld->deadline = args->deadline;
+  calld->cancel_error = GRPC_ERROR_NONE;
+  gpr_atm_rel_store(&calld->subchannel_call, 0);
+  gpr_mu_init(&calld->mu);
+  calld->connected_subchannel = NULL;
+  calld->waiting_ops = NULL;
+  calld->waiting_ops_count = 0;
+  calld->waiting_ops_capacity = 0;
+  calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
+  calld->owning_call = args->call_stack;
+  calld->pollent = NULL;
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for call_data */
-static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats,
-                              void *and_free_memory) {
-  grpc_subchannel_call_holder_destroy(exec_ctx, elem->call_data);
+static void cc_destroy_call_elem(grpc_exec_ctx *exec_ctx,
+                                 grpc_call_element *elem,
+                                 const grpc_call_final_info *final_info,
+                                 void *and_free_memory) {
+  call_data *calld = elem->call_data;
+  grpc_deadline_state_destroy(exec_ctx, elem);
+  GRPC_ERROR_UNREF(calld->cancel_error);
+  grpc_subchannel_call *call = GET_CALL(calld);
+  if (call != NULL && call != CANCELLED_CALL) {
+    GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, call, "client_channel_destroy_call");
+  }
+  GPR_ASSERT(calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING);
+  gpr_mu_destroy(&calld->mu);
+  GPR_ASSERT(calld->waiting_ops_count == 0);
+  gpr_free(calld->waiting_ops);
   gpr_free(and_free_memory);
 }
 
-/* Constructor for channel_data */
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
-                              grpc_channel_element *elem,
-                              grpc_channel_element_args *args) {
-  channel_data *chand = elem->channel_data;
-
-  memset(chand, 0, sizeof(*chand));
-
-  GPR_ASSERT(args->is_last);
-  GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
-
-  gpr_mu_init(&chand->mu_config);
-  grpc_closure_init(&chand->on_config_changed, cc_on_config_changed, chand);
-  chand->owning_stack = args->channel_stack;
-
-  grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE,
-                               "client_channel");
-  chand->interested_parties = grpc_pollset_set_create();
-}
-
-/* Destructor for channel_data */
-static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
-                                 grpc_channel_element *elem) {
-  channel_data *chand = elem->channel_data;
-
-  if (chand->resolver != NULL) {
-    grpc_resolver_shutdown(exec_ctx, chand->resolver);
-    GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel");
-  }
-  if (chand->lb_policy != NULL) {
-    grpc_pollset_set_del_pollset_set(exec_ctx,
-                                     chand->lb_policy->interested_parties,
-                                     chand->interested_parties);
-    GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel");
-  }
-  grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker);
-  grpc_pollset_set_destroy(chand->interested_parties);
-  gpr_mu_destroy(&chand->mu_config);
-}
-
 static void cc_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
                                           grpc_call_element *elem,
                                           grpc_polling_entity *pollent) {
@@ -497,61 +818,68 @@
   calld->pollent = pollent;
 }
 
+/*************************************************************************
+ * EXPORTED SYMBOLS
+ */
+
 const grpc_channel_filter grpc_client_channel_filter = {
     cc_start_transport_stream_op,
     cc_start_transport_op,
     sizeof(call_data),
-    init_call_elem,
+    cc_init_call_elem,
     cc_set_pollset_or_pollset_set,
-    destroy_call_elem,
+    cc_destroy_call_elem,
     sizeof(channel_data),
-    init_channel_elem,
-    destroy_channel_elem,
+    cc_init_channel_elem,
+    cc_destroy_channel_elem,
     cc_get_peer,
     "client-channel",
 };
 
-void grpc_client_channel_set_resolver(grpc_exec_ctx *exec_ctx,
-                                      grpc_channel_stack *channel_stack,
-                                      grpc_resolver *resolver) {
+void grpc_client_channel_finish_initialization(
+    grpc_exec_ctx *exec_ctx, grpc_channel_stack *channel_stack,
+    grpc_resolver *resolver,
+    grpc_client_channel_factory *client_channel_factory) {
   /* post construction initialization: set the transport setup pointer */
+  GPR_ASSERT(client_channel_factory != NULL);
   grpc_channel_element *elem = grpc_channel_stack_last_element(channel_stack);
   channel_data *chand = elem->channel_data;
-  gpr_mu_lock(&chand->mu_config);
+  gpr_mu_lock(&chand->mu);
   GPR_ASSERT(!chand->resolver);
   chand->resolver = resolver;
   GRPC_RESOLVER_REF(resolver, "channel");
   if (!grpc_closure_list_empty(chand->waiting_for_config_closures) ||
       chand->exit_idle_when_lb_policy_arrives) {
-    chand->started_resolving = 1;
+    chand->started_resolving = true;
     GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver");
-    grpc_resolver_next(exec_ctx, resolver, &chand->incoming_configuration,
-                       &chand->on_config_changed);
+    grpc_resolver_next(exec_ctx, resolver, &chand->resolver_result,
+                       &chand->on_resolver_result_changed);
   }
-  gpr_mu_unlock(&chand->mu_config);
+  chand->client_channel_factory = client_channel_factory;
+  grpc_client_channel_factory_ref(client_channel_factory);
+  gpr_mu_unlock(&chand->mu);
 }
 
 grpc_connectivity_state grpc_client_channel_check_connectivity_state(
     grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect) {
   channel_data *chand = elem->channel_data;
   grpc_connectivity_state out;
-  gpr_mu_lock(&chand->mu_config);
+  gpr_mu_lock(&chand->mu);
   out = grpc_connectivity_state_check(&chand->state_tracker, NULL);
   if (out == GRPC_CHANNEL_IDLE && try_to_connect) {
     if (chand->lb_policy != NULL) {
       grpc_lb_policy_exit_idle(exec_ctx, chand->lb_policy);
     } else {
-      chand->exit_idle_when_lb_policy_arrives = 1;
+      chand->exit_idle_when_lb_policy_arrives = true;
       if (!chand->started_resolving && chand->resolver != NULL) {
         GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver");
-        chand->started_resolving = 1;
-        grpc_resolver_next(exec_ctx, chand->resolver,
-                           &chand->incoming_configuration,
-                           &chand->on_config_changed);
+        chand->started_resolving = true;
+        grpc_resolver_next(exec_ctx, chand->resolver, &chand->resolver_result,
+                           &chand->on_resolver_result_changed);
       }
     }
   }
-  gpr_mu_unlock(&chand->mu_config);
+  gpr_mu_unlock(&chand->mu);
   return out;
 }
 
@@ -586,8 +914,8 @@
   grpc_closure_init(&w->my_closure, on_external_watch_complete, w);
   GRPC_CHANNEL_STACK_REF(w->chand->owning_stack,
                          "external_connectivity_watcher");
-  gpr_mu_lock(&chand->mu_config);
+  gpr_mu_lock(&chand->mu);
   grpc_connectivity_state_notify_on_state_change(
       exec_ctx, &chand->state_tracker, state, &w->my_closure);
-  gpr_mu_unlock(&chand->mu_config);
+  gpr_mu_unlock(&chand->mu);
 }
diff --git a/src/core/ext/client_config/client_channel.h b/src/core/ext/client_config/client_channel.h
index 1e47ad3..abb5ac4 100644
--- a/src/core/ext/client_config/client_channel.h
+++ b/src/core/ext/client_config/client_channel.h
@@ -34,6 +34,7 @@
 #ifndef GRPC_CORE_EXT_CLIENT_CONFIG_CLIENT_CHANNEL_H
 #define GRPC_CORE_EXT_CLIENT_CONFIG_CLIENT_CHANNEL_H
 
+#include "src/core/ext/client_config/client_channel_factory.h"
 #include "src/core/ext/client_config/resolver.h"
 #include "src/core/lib/channel/channel_stack.h"
 
@@ -46,12 +47,12 @@
 
 extern const grpc_channel_filter grpc_client_channel_filter;
 
-/* post-construction initializer to let the client channel know which
-   transport setup it should cancel upon destruction, or initiate when it needs
-   a connection */
-void grpc_client_channel_set_resolver(grpc_exec_ctx *exec_ctx,
-                                      grpc_channel_stack *channel_stack,
-                                      grpc_resolver *resolver);
+/* Post-construction initializer to give the client channel its resolver
+   and factory. */
+void grpc_client_channel_finish_initialization(
+    grpc_exec_ctx *exec_ctx, grpc_channel_stack *channel_stack,
+    grpc_resolver *resolver,
+    grpc_client_channel_factory *client_channel_factory);
 
 grpc_connectivity_state grpc_client_channel_check_connectivity_state(
     grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect);
diff --git a/src/core/ext/client_config/client_config.c b/src/core/ext/client_config/client_config.c
deleted file mode 100644
index f9b8e68..0000000
--- a/src/core/ext/client_config/client_config.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include "src/core/ext/client_config/client_config.h"
-
-#include <string.h>
-
-#include <grpc/support/alloc.h>
-
-struct grpc_client_config {
-  gpr_refcount refs;
-  grpc_lb_policy *lb_policy;
-};
-
-grpc_client_config *grpc_client_config_create() {
-  grpc_client_config *c = gpr_malloc(sizeof(*c));
-  memset(c, 0, sizeof(*c));
-  gpr_ref_init(&c->refs, 1);
-  return c;
-}
-
-void grpc_client_config_ref(grpc_client_config *c) { gpr_ref(&c->refs); }
-
-void grpc_client_config_unref(grpc_exec_ctx *exec_ctx, grpc_client_config *c) {
-  if (gpr_unref(&c->refs)) {
-    if (c->lb_policy != NULL) {
-      GRPC_LB_POLICY_UNREF(exec_ctx, c->lb_policy, "client_config");
-    }
-    gpr_free(c);
-  }
-}
-
-void grpc_client_config_set_lb_policy(grpc_client_config *c,
-                                      grpc_lb_policy *lb_policy) {
-  GPR_ASSERT(c->lb_policy == NULL);
-  if (lb_policy) {
-    GRPC_LB_POLICY_REF(lb_policy, "client_config");
-  }
-  c->lb_policy = lb_policy;
-}
-
-grpc_lb_policy *grpc_client_config_get_lb_policy(grpc_client_config *c) {
-  return c->lb_policy;
-}
diff --git a/src/core/ext/client_config/client_config.h b/src/core/ext/client_config/client_config.h
deleted file mode 100644
index a6290cb..0000000
--- a/src/core/ext/client_config/client_config.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_CLIENT_CONFIG_H
-#define GRPC_CORE_EXT_CLIENT_CONFIG_CLIENT_CONFIG_H
-
-#include "src/core/ext/client_config/lb_policy.h"
-
-/** Total configuration for a client. Provided, and updated, by
-    grpc_resolver */
-typedef struct grpc_client_config grpc_client_config;
-
-grpc_client_config *grpc_client_config_create();
-void grpc_client_config_ref(grpc_client_config *client_config);
-void grpc_client_config_unref(grpc_exec_ctx *exec_ctx,
-                              grpc_client_config *client_config);
-
-void grpc_client_config_set_lb_policy(grpc_client_config *client_config,
-                                      grpc_lb_policy *lb_policy);
-grpc_lb_policy *grpc_client_config_get_lb_policy(
-    grpc_client_config *client_config);
-
-#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_CLIENT_CONFIG_H */
diff --git a/src/core/ext/client_config/client_config_plugin.c b/src/core/ext/client_config/client_config_plugin.c
index 5e31613..dc3629d 100644
--- a/src/core/ext/client_config/client_config_plugin.c
+++ b/src/core/ext/client_config/client_config_plugin.c
@@ -43,10 +43,6 @@
 #include "src/core/ext/client_config/subchannel_index.h"
 #include "src/core/lib/surface/channel_init.h"
 
-#ifndef GRPC_DEFAULT_NAME_PREFIX
-#define GRPC_DEFAULT_NAME_PREFIX "dns:///"
-#endif
-
 static bool append_filter(grpc_channel_stack_builder *builder, void *arg) {
   return grpc_channel_stack_builder_append_filter(
       builder, (const grpc_channel_filter *)arg, NULL, NULL);
@@ -79,7 +75,7 @@
 
 void grpc_client_config_init(void) {
   grpc_lb_policy_registry_init();
-  grpc_resolver_registry_init(GRPC_DEFAULT_NAME_PREFIX);
+  grpc_resolver_registry_init();
   grpc_subchannel_index_init();
   grpc_channel_init_register_stage(GRPC_CLIENT_CHANNEL, INT_MIN,
                                    set_default_host_if_unset, NULL);
diff --git a/src/core/ext/client_config/http_connect_handshaker.c b/src/core/ext/client_config/http_connect_handshaker.c
new file mode 100644
index 0000000..fda1df1
--- /dev/null
+++ b/src/core/ext/client_config/http_connect_handshaker.c
@@ -0,0 +1,275 @@
+/*
+ *
+ * Copyright 2016, 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/ext/client_config/http_connect_handshaker.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice_buffer.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/client_config/uri_parser.h"
+#include "src/core/lib/http/format_request.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/support/env.h"
+
+typedef struct http_connect_handshaker {
+  // Base class.  Must be first.
+  grpc_handshaker base;
+
+  char* proxy_server;
+  char* server_name;
+
+  // State saved while performing the handshake.
+  grpc_endpoint* endpoint;
+  grpc_channel_args* args;
+  grpc_handshaker_done_cb cb;
+  void* user_data;
+
+  // Objects for processing the HTTP CONNECT request and response.
+  gpr_slice_buffer write_buffer;
+  gpr_slice_buffer* read_buffer;  // Ownership passes through this object.
+  grpc_closure request_done_closure;
+  grpc_closure response_read_closure;
+  grpc_http_parser http_parser;
+  grpc_http_response http_response;
+  grpc_timer timeout_timer;
+
+  gpr_refcount refcount;
+} http_connect_handshaker;
+
+// Unref and clean up handshaker.
+static void http_connect_handshaker_unref(http_connect_handshaker* handshaker) {
+  if (gpr_unref(&handshaker->refcount)) {
+    gpr_free(handshaker->proxy_server);
+    gpr_free(handshaker->server_name);
+    gpr_slice_buffer_destroy(&handshaker->write_buffer);
+    grpc_http_parser_destroy(&handshaker->http_parser);
+    grpc_http_response_destroy(&handshaker->http_response);
+    gpr_free(handshaker);
+  }
+}
+
+// Callback invoked when deadline is exceeded.
+static void on_timeout(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) {
+  http_connect_handshaker* handshaker = arg;
+  if (error == GRPC_ERROR_NONE) {  // Timer fired, rather than being cancelled.
+    grpc_endpoint_shutdown(exec_ctx, handshaker->endpoint);
+  }
+  http_connect_handshaker_unref(handshaker);
+}
+
+// Callback invoked when finished writing HTTP CONNECT request.
+static void on_write_done(grpc_exec_ctx* exec_ctx, void* arg,
+                          grpc_error* error) {
+  http_connect_handshaker* handshaker = arg;
+  if (error != GRPC_ERROR_NONE) {
+    // If the write failed, invoke the callback immediately with the error.
+    handshaker->cb(exec_ctx, handshaker->endpoint, handshaker->args,
+                   handshaker->read_buffer, handshaker->user_data,
+                   GRPC_ERROR_REF(error));
+  } else {
+    // Otherwise, read the response.
+    grpc_endpoint_read(exec_ctx, handshaker->endpoint, handshaker->read_buffer,
+                       &handshaker->response_read_closure);
+  }
+}
+
+// Callback invoked for reading HTTP CONNECT response.
+static void on_read_done(grpc_exec_ctx* exec_ctx, void* arg,
+                         grpc_error* error) {
+  http_connect_handshaker* handshaker = arg;
+  if (error != GRPC_ERROR_NONE) {
+    GRPC_ERROR_REF(error);  // Take ref to pass to the handshake-done callback.
+    goto done;
+  }
+  // Add buffer to parser.
+  for (size_t i = 0; i < handshaker->read_buffer->count; ++i) {
+    if (GPR_SLICE_LENGTH(handshaker->read_buffer->slices[i]) > 0) {
+      size_t body_start_offset = 0;
+      error = grpc_http_parser_parse(&handshaker->http_parser,
+                                     handshaker->read_buffer->slices[i],
+                                     &body_start_offset);
+      if (error != GRPC_ERROR_NONE) goto done;
+      if (handshaker->http_parser.state == GRPC_HTTP_BODY) {
+        // We've gotten back a successul response, so stop the timeout timer.
+        grpc_timer_cancel(exec_ctx, &handshaker->timeout_timer);
+        // Remove the data we've already read from the read buffer,
+        // leaving only the leftover bytes (if any).
+        gpr_slice_buffer tmp_buffer;
+        gpr_slice_buffer_init(&tmp_buffer);
+        if (body_start_offset <
+            GPR_SLICE_LENGTH(handshaker->read_buffer->slices[i])) {
+          gpr_slice_buffer_add(
+              &tmp_buffer,
+              gpr_slice_split_tail(&handshaker->read_buffer->slices[i],
+                                   body_start_offset));
+        }
+        gpr_slice_buffer_addn(&tmp_buffer,
+                              &handshaker->read_buffer->slices[i + 1],
+                              handshaker->read_buffer->count - i - 1);
+        gpr_slice_buffer_swap(handshaker->read_buffer, &tmp_buffer);
+        gpr_slice_buffer_destroy(&tmp_buffer);
+        break;
+      }
+    }
+  }
+  // If we're not done reading the response, read more data.
+  // TODO(roth): In practice, I suspect that the response to a CONNECT
+  // request will never include a body, in which case this check is
+  // sufficient.  However, the language of RFC-2817 doesn't explicitly
+  // forbid the response from including a body.  If there is a body,
+  // it's possible that we might have parsed part but not all of the
+  // body, in which case this check will cause us to fail to parse the
+  // remainder of the body.  If that ever becomes an issue, we may
+  // need to fix the HTTP parser to understand when the body is
+  // complete (e.g., handling chunked transfer encoding or looking
+  // at the Content-Length: header).
+  if (handshaker->http_parser.state != GRPC_HTTP_BODY) {
+    gpr_slice_buffer_reset_and_unref(handshaker->read_buffer);
+    grpc_endpoint_read(exec_ctx, handshaker->endpoint, handshaker->read_buffer,
+                       &handshaker->response_read_closure);
+    return;
+  }
+  // Make sure we got a 2xx response.
+  if (handshaker->http_response.status < 200 ||
+      handshaker->http_response.status >= 300) {
+    char* msg;
+    gpr_asprintf(&msg, "HTTP proxy returned response code %d",
+                 handshaker->http_response.status);
+    error = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
+  }
+done:
+  // Invoke handshake-done callback.
+  handshaker->cb(exec_ctx, handshaker->endpoint, handshaker->args,
+                 handshaker->read_buffer, handshaker->user_data, error);
+}
+
+//
+// Public handshaker methods
+//
+
+static void http_connect_handshaker_destroy(grpc_exec_ctx* exec_ctx,
+                                            grpc_handshaker* handshaker_in) {
+  http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in;
+  http_connect_handshaker_unref(handshaker);
+}
+
+static void http_connect_handshaker_shutdown(grpc_exec_ctx* exec_ctx,
+                                             grpc_handshaker* handshaker) {}
+
+static void http_connect_handshaker_do_handshake(
+    grpc_exec_ctx* exec_ctx, grpc_handshaker* handshaker_in,
+    grpc_endpoint* endpoint, grpc_channel_args* args,
+    gpr_slice_buffer* read_buffer, gpr_timespec deadline,
+    grpc_tcp_server_acceptor* acceptor, grpc_handshaker_done_cb cb,
+    void* user_data) {
+  http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in;
+  // Save state in the handshaker object.
+  handshaker->endpoint = endpoint;
+  handshaker->args = args;
+  handshaker->cb = cb;
+  handshaker->user_data = user_data;
+  handshaker->read_buffer = read_buffer;
+  // Send HTTP CONNECT request.
+  gpr_log(GPR_INFO, "Connecting to server %s via HTTP proxy %s",
+          handshaker->server_name, handshaker->proxy_server);
+  grpc_httpcli_request request;
+  memset(&request, 0, sizeof(request));
+  request.host = handshaker->proxy_server;
+  request.http.method = "CONNECT";
+  request.http.path = handshaker->server_name;
+  request.handshaker = &grpc_httpcli_plaintext;
+  gpr_slice request_slice = grpc_httpcli_format_connect_request(&request);
+  gpr_slice_buffer_add(&handshaker->write_buffer, request_slice);
+  grpc_endpoint_write(exec_ctx, endpoint, &handshaker->write_buffer,
+                      &handshaker->request_done_closure);
+  // Set timeout timer.  The timer gets a reference to the handshaker.
+  gpr_ref(&handshaker->refcount);
+  grpc_timer_init(exec_ctx, &handshaker->timeout_timer,
+                  gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
+                  on_timeout, handshaker, gpr_now(GPR_CLOCK_MONOTONIC));
+}
+
+static const struct grpc_handshaker_vtable http_connect_handshaker_vtable = {
+    http_connect_handshaker_destroy, http_connect_handshaker_shutdown,
+    http_connect_handshaker_do_handshake};
+
+grpc_handshaker* grpc_http_connect_handshaker_create(const char* proxy_server,
+                                                     const char* server_name) {
+  GPR_ASSERT(proxy_server != NULL);
+  GPR_ASSERT(server_name != NULL);
+  http_connect_handshaker* handshaker =
+      gpr_malloc(sizeof(http_connect_handshaker));
+  memset(handshaker, 0, sizeof(*handshaker));
+  grpc_handshaker_init(&http_connect_handshaker_vtable, &handshaker->base);
+  handshaker->proxy_server = gpr_strdup(proxy_server);
+  handshaker->server_name = gpr_strdup(server_name);
+  gpr_slice_buffer_init(&handshaker->write_buffer);
+  grpc_closure_init(&handshaker->request_done_closure, on_write_done,
+                    handshaker);
+  grpc_closure_init(&handshaker->response_read_closure, on_read_done,
+                    handshaker);
+  grpc_http_parser_init(&handshaker->http_parser, GRPC_HTTP_RESPONSE,
+                        &handshaker->http_response);
+  gpr_ref_init(&handshaker->refcount, 1);
+  return &handshaker->base;
+}
+
+char* grpc_get_http_proxy_server() {
+  char* uri_str = gpr_getenv("http_proxy");
+  if (uri_str == NULL) return NULL;
+  grpc_uri* uri = grpc_uri_parse(uri_str, false /* suppress_errors */);
+  char* proxy_name = NULL;
+  if (uri == NULL || uri->authority == NULL) {
+    gpr_log(GPR_ERROR, "cannot parse value of 'http_proxy' env var");
+    goto done;
+  }
+  if (strcmp(uri->scheme, "http") != 0) {
+    gpr_log(GPR_ERROR, "'%s' scheme not supported in proxy URI", uri->scheme);
+    goto done;
+  }
+  if (strchr(uri->authority, '@') != NULL) {
+    gpr_log(GPR_ERROR, "userinfo not supported in proxy URI");
+    goto done;
+  }
+  proxy_name = gpr_strdup(uri->authority);
+done:
+  gpr_free(uri_str);
+  grpc_uri_destroy(uri);
+  return proxy_name;
+}
diff --git a/src/core/ext/client_config/http_connect_handshaker.h b/src/core/ext/client_config/http_connect_handshaker.h
new file mode 100644
index 0000000..1fc3948
--- /dev/null
+++ b/src/core/ext/client_config/http_connect_handshaker.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2016, 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_CORE_EXT_CLIENT_CONFIG_HTTP_CONNECT_HANDSHAKER_H
+#define GRPC_CORE_EXT_CLIENT_CONFIG_HTTP_CONNECT_HANDSHAKER_H
+
+#include "src/core/lib/channel/handshaker.h"
+
+/// Does NOT take ownership of \a proxy_server or \a server_name.
+grpc_handshaker* grpc_http_connect_handshaker_create(const char* proxy_server,
+                                                     const char* server_name);
+
+/// Returns the name of the proxy to use, or NULL if no proxy is configured.
+/// Caller takes ownership of result.
+char* grpc_get_http_proxy_server();
+
+#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_HTTP_CONNECT_HANDSHAKER_H */
diff --git a/src/core/ext/client_config/lb_policy.c b/src/core/ext/client_config/lb_policy.c
index 8b980b2..4639127 100644
--- a/src/core/ext/client_config/lb_policy.c
+++ b/src/core/ext/client_config/lb_policy.c
@@ -100,26 +100,26 @@
 }
 
 int grpc_lb_policy_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
-                        grpc_polling_entity *pollent,
-                        grpc_metadata_batch *initial_metadata,
-                        uint32_t initial_metadata_flags,
-                        grpc_connected_subchannel **target,
+                        const grpc_lb_policy_pick_args *pick_args,
+                        grpc_connected_subchannel **target, void **user_data,
                         grpc_closure *on_complete) {
-  return policy->vtable->pick(exec_ctx, policy, pollent, initial_metadata,
-                              initial_metadata_flags, target, on_complete);
+  return policy->vtable->pick(exec_ctx, policy, pick_args, target, user_data,
+                              on_complete);
 }
 
 void grpc_lb_policy_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
-                                grpc_connected_subchannel **target) {
-  policy->vtable->cancel_pick(exec_ctx, policy, target);
+                                grpc_connected_subchannel **target,
+                                grpc_error *error) {
+  policy->vtable->cancel_pick(exec_ctx, policy, target, error);
 }
 
 void grpc_lb_policy_cancel_picks(grpc_exec_ctx *exec_ctx,
                                  grpc_lb_policy *policy,
                                  uint32_t initial_metadata_flags_mask,
-                                 uint32_t initial_metadata_flags_eq) {
+                                 uint32_t initial_metadata_flags_eq,
+                                 grpc_error *error) {
   policy->vtable->cancel_picks(exec_ctx, policy, initial_metadata_flags_mask,
-                               initial_metadata_flags_eq);
+                               initial_metadata_flags_eq, error);
 }
 
 void grpc_lb_policy_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy) {
diff --git a/src/core/ext/client_config/lb_policy.h b/src/core/ext/client_config/lb_policy.h
index 3cfd041..6cc3e1e 100644
--- a/src/core/ext/client_config/lb_policy.h
+++ b/src/core/ext/client_config/lb_policy.h
@@ -53,27 +53,46 @@
   grpc_pollset_set *interested_parties;
 };
 
+/** Extra arguments for an LB pick */
+typedef struct grpc_lb_policy_pick_args {
+  /** Parties interested in the pick's progress */
+  grpc_polling_entity *pollent;
+  /** Initial metadata associated with the picking call. */
+  grpc_metadata_batch *initial_metadata;
+  /** Bitmask used for selective cancelling. See \a
+   * grpc_lb_policy_cancel_picks() and \a GRPC_INITIAL_METADATA_* in
+   * grpc_types.h */
+  uint32_t initial_metadata_flags;
+  /** Storage for LB token in \a initial_metadata, or NULL if not used */
+  grpc_linked_mdelem *lb_token_mdelem_storage;
+  /** Deadline for the call to the LB server */
+  gpr_timespec deadline;
+} grpc_lb_policy_pick_args;
+
 struct grpc_lb_policy_vtable {
   void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy);
-
   void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy);
 
-  /** implement grpc_lb_policy_pick */
+  /** \see grpc_lb_policy_pick */
   int (*pick)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
-              grpc_polling_entity *pollent,
-              grpc_metadata_batch *initial_metadata,
-              uint32_t initial_metadata_flags,
-              grpc_connected_subchannel **target, grpc_closure *on_complete);
+              const grpc_lb_policy_pick_args *pick_args,
+              grpc_connected_subchannel **target, void **user_data,
+              grpc_closure *on_complete);
+
+  /** \see grpc_lb_policy_cancel_pick */
   void (*cancel_pick)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
-                      grpc_connected_subchannel **target);
+                      grpc_connected_subchannel **target, grpc_error *error);
+
+  /** \see grpc_lb_policy_cancel_picks */
   void (*cancel_picks)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
                        uint32_t initial_metadata_flags_mask,
-                       uint32_t initial_metadata_flags_eq);
+                       uint32_t initial_metadata_flags_eq, grpc_error *error);
 
+  /** \see grpc_lb_policy_ping_one */
   void (*ping_one)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
                    grpc_closure *closure);
 
-  /** try to enter a READY connectivity state */
+  /** Try to enter a READY connectivity state */
   void (*exit_idle)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy);
 
   /** check the current connectivity of the lb_policy */
@@ -82,7 +101,8 @@
       grpc_error **connectivity_error);
 
   /** call notify when the connectivity state of a channel changes from *state.
-      Updates *state with the new state of the policy */
+      Updates *state with the new state of the policy. Calling with a NULL \a
+      state cancels the subscription.  */
   void (*notify_on_state_change)(grpc_exec_ctx *exec_ctx,
                                  grpc_lb_policy *policy,
                                  grpc_connectivity_state *state,
@@ -122,33 +142,46 @@
 void grpc_lb_policy_init(grpc_lb_policy *policy,
                          const grpc_lb_policy_vtable *vtable);
 
-/** Given initial metadata in \a initial_metadata, find an appropriate
-    target for this rpc, and 'return' it by calling \a on_complete after setting
-    \a target.
-    Picking can be asynchronous. Any IO should be done under \a pollset. */
+/** Find an appropriate target for this call, based on \a pick_args.
+    Picking can be synchronous or asynchronous. In the synchronous case, when a
+    pick is readily available, it'll be returned in \a target and a non-zero
+    value will be returned.
+    In the asynchronous case, zero is returned and \a on_complete will be called
+    once \a target and \a user_data have been set. Any IO should be done under
+    \a pick_args->pollent. The opaque \a user_data output argument corresponds
+    to information that may need be propagated from the LB policy. It may be
+    NULL. Errors are signaled by receiving a NULL \a *target. */
 int grpc_lb_policy_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
-                        grpc_polling_entity *pollent,
-                        grpc_metadata_batch *initial_metadata,
-                        uint32_t initial_metadata_flags,
-                        grpc_connected_subchannel **target,
+                        const grpc_lb_policy_pick_args *pick_args,
+                        grpc_connected_subchannel **target, void **user_data,
                         grpc_closure *on_complete);
 
+/** Perform a connected subchannel ping (see \a grpc_connected_subchannel_ping)
+    against one of the connected subchannels managed by \a policy. */
 void grpc_lb_policy_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
                              grpc_closure *closure);
 
+/** Cancel picks for \a target.
+    The \a on_complete callback of the pending picks will be invoked with \a
+    *target set to NULL. */
 void grpc_lb_policy_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
-                                grpc_connected_subchannel **target);
+                                grpc_connected_subchannel **target,
+                                grpc_error *error);
 
-/** Cancel all pending picks which have:
-    (initial_metadata_flags & initial_metadata_flags_mask) ==
-         initial_metadata_flags_eq */
+/** Cancel all pending picks for which their \a initial_metadata_flags (as given
+    in the call to \a grpc_lb_policy_pick) matches \a initial_metadata_flags_eq
+    when AND'd with \a initial_metadata_flags_mask */
 void grpc_lb_policy_cancel_picks(grpc_exec_ctx *exec_ctx,
                                  grpc_lb_policy *policy,
                                  uint32_t initial_metadata_flags_mask,
-                                 uint32_t initial_metadata_flags_eq);
+                                 uint32_t initial_metadata_flags_eq,
+                                 grpc_error *error);
 
+/** Try to enter a READY connectivity state */
 void grpc_lb_policy_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy);
 
+/* Call notify when the connectivity state of a channel changes from \a *state.
+ * Updates \a *state with the new state of the policy */
 void grpc_lb_policy_notify_on_state_change(grpc_exec_ctx *exec_ctx,
                                            grpc_lb_policy *policy,
                                            grpc_connectivity_state *state,
diff --git a/src/core/ext/client_config/lb_policy_factory.c b/src/core/ext/client_config/lb_policy_factory.c
index 70e46ef..c17af91 100644
--- a/src/core/ext/client_config/lb_policy_factory.c
+++ b/src/core/ext/client_config/lb_policy_factory.c
@@ -31,8 +31,66 @@
  *
  */
 
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
 #include "src/core/ext/client_config/lb_policy_factory.h"
 
+grpc_lb_addresses* grpc_lb_addresses_create(size_t num_addresses) {
+  grpc_lb_addresses* addresses = gpr_malloc(sizeof(grpc_lb_addresses));
+  addresses->num_addresses = num_addresses;
+  const size_t addresses_size = sizeof(grpc_lb_address) * num_addresses;
+  addresses->addresses = gpr_malloc(addresses_size);
+  memset(addresses->addresses, 0, addresses_size);
+  return addresses;
+}
+
+grpc_lb_addresses* grpc_lb_addresses_copy(grpc_lb_addresses* addresses,
+                                          void* (*user_data_copy)(void*)) {
+  grpc_lb_addresses* new_addresses =
+      grpc_lb_addresses_create(addresses->num_addresses);
+  memcpy(new_addresses->addresses, addresses->addresses,
+         sizeof(grpc_lb_address) * addresses->num_addresses);
+  for (size_t i = 0; i < addresses->num_addresses; ++i) {
+    if (new_addresses->addresses[i].balancer_name != NULL) {
+      new_addresses->addresses[i].balancer_name =
+          gpr_strdup(new_addresses->addresses[i].balancer_name);
+    }
+    if (user_data_copy != NULL) {
+      new_addresses->addresses[i].user_data =
+          user_data_copy(new_addresses->addresses[i].user_data);
+    }
+  }
+  return new_addresses;
+}
+
+void grpc_lb_addresses_set_address(grpc_lb_addresses* addresses, size_t index,
+                                   void* address, size_t address_len,
+                                   bool is_balancer, char* balancer_name,
+                                   void* user_data) {
+  GPR_ASSERT(index < addresses->num_addresses);
+  grpc_lb_address* target = &addresses->addresses[index];
+  memcpy(target->address.addr, address, address_len);
+  target->address.len = address_len;
+  target->is_balancer = is_balancer;
+  target->balancer_name = balancer_name;
+  target->user_data = user_data;
+}
+
+void grpc_lb_addresses_destroy(grpc_lb_addresses* addresses,
+                               void (*user_data_destroy)(void*)) {
+  for (size_t i = 0; i < addresses->num_addresses; ++i) {
+    gpr_free(addresses->addresses[i].balancer_name);
+    if (user_data_destroy != NULL) {
+      user_data_destroy(addresses->addresses[i].user_data);
+    }
+  }
+  gpr_free(addresses->addresses);
+  gpr_free(addresses);
+}
+
 void grpc_lb_policy_factory_ref(grpc_lb_policy_factory* factory) {
   factory->vtable->ref(factory);
 }
diff --git a/src/core/ext/client_config/lb_policy_factory.h b/src/core/ext/client_config/lb_policy_factory.h
index 1c89b28..ade5570 100644
--- a/src/core/ext/client_config/lb_policy_factory.h
+++ b/src/core/ext/client_config/lb_policy_factory.h
@@ -36,22 +36,65 @@
 
 #include "src/core/ext/client_config/client_channel_factory.h"
 #include "src/core/ext/client_config/lb_policy.h"
-#include "src/core/lib/iomgr/resolve_address.h"
 
 #include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/resolve_address.h"
 
 typedef struct grpc_lb_policy_factory grpc_lb_policy_factory;
 typedef struct grpc_lb_policy_factory_vtable grpc_lb_policy_factory_vtable;
 
-/** grpc_lb_policy provides grpc_client_config objects to grpc_channel
-    objects */
 struct grpc_lb_policy_factory {
   const grpc_lb_policy_factory_vtable *vtable;
 };
 
+/** A resolved address alongside any LB related information associated with it.
+ * \a user_data, if not NULL, contains opaque data meant to be consumed by the
+ * gRPC LB policy. Note that no all LB policies support \a user_data as input.
+ * Those who don't will simply ignore it and will correspondingly return NULL in
+ * their namesake pick() output argument. */
+typedef struct grpc_lb_address {
+  grpc_resolved_address address;
+  bool is_balancer;
+  char *balancer_name; /* For secure naming. */
+  void *user_data;
+} grpc_lb_address;
+
+typedef struct grpc_lb_addresses {
+  size_t num_addresses;
+  grpc_lb_address *addresses;
+} grpc_lb_addresses;
+
+/** Returns a grpc_addresses struct with enough space for
+ * \a num_addresses addresses. */
+grpc_lb_addresses *grpc_lb_addresses_create(size_t num_addresses);
+
+/** Creates a copy of \a addresses.  If \a user_data_copy is not NULL,
+ * it will be invoked to copy the \a user_data field of each address. */
+grpc_lb_addresses *grpc_lb_addresses_copy(grpc_lb_addresses *addresses,
+                                          void *(*user_data_copy)(void *));
+
+/** Sets the value of the address at index \a index of \a addresses.
+ * \a address is a socket address of length \a address_len.
+ * Takes ownership of \a balancer_name. */
+void grpc_lb_addresses_set_address(grpc_lb_addresses *addresses, size_t index,
+                                   void *address, size_t address_len,
+                                   bool is_balancer, char *balancer_name,
+                                   void *user_data);
+
+/** Destroys \a addresses.  If \a user_data_destroy is not NULL, it will
+ * be invoked to destroy the \a user_data field of each address. */
+void grpc_lb_addresses_destroy(grpc_lb_addresses *addresses,
+                               void (*user_data_destroy)(void *));
+
+/** Arguments passed to LB policies. */
+/* TODO(roth, ctiller): Consider replacing this struct with
+   grpc_channel_args.  See comment in resolver_result.h for details. */
 typedef struct grpc_lb_policy_args {
-  grpc_resolved_addresses *addresses;
+  const char *server_name;
+  grpc_lb_addresses *addresses;
   grpc_client_channel_factory *client_channel_factory;
+  /* Can be used to pass implementation-specific parameters to the LB policy. */
+  grpc_channel_args *additional_args;
 } grpc_lb_policy_args;
 
 struct grpc_lb_policy_factory_vtable {
diff --git a/src/core/ext/client_config/resolver.c b/src/core/ext/client_config/resolver.c
index eb00445..7534ea6 100644
--- a/src/core/ext/client_config/resolver.c
+++ b/src/core/ext/client_config/resolver.c
@@ -76,7 +76,7 @@
 }
 
 void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver,
-                        grpc_client_config **target_config,
+                        grpc_resolver_result **result,
                         grpc_closure *on_complete) {
-  resolver->vtable->next(exec_ctx, resolver, target_config, on_complete);
+  resolver->vtable->next(exec_ctx, resolver, result, on_complete);
 }
diff --git a/src/core/ext/client_config/resolver.h b/src/core/ext/client_config/resolver.h
index 6ecb5d2..88ac262 100644
--- a/src/core/ext/client_config/resolver.h
+++ b/src/core/ext/client_config/resolver.h
@@ -34,14 +34,14 @@
 #ifndef GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_H
 #define GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_H
 
-#include "src/core/ext/client_config/client_config.h"
+#include "src/core/ext/client_config/resolver_result.h"
 #include "src/core/ext/client_config/subchannel.h"
 #include "src/core/lib/iomgr/iomgr.h"
 
 typedef struct grpc_resolver grpc_resolver;
 typedef struct grpc_resolver_vtable grpc_resolver_vtable;
 
-/** grpc_resolver provides grpc_client_config objects to grpc_channel
+/** grpc_resolver provides grpc_resolver_result objects to grpc_channel
     objects */
 struct grpc_resolver {
   const grpc_resolver_vtable *vtable;
@@ -53,7 +53,7 @@
   void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver);
   void (*channel_saw_error)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver);
   void (*next)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver,
-               grpc_client_config **target_config, grpc_closure *on_complete);
+               grpc_resolver_result **result, grpc_closure *on_complete);
 };
 
 #ifdef GRPC_RESOLVER_REFCOUNT_DEBUG
@@ -82,13 +82,13 @@
                                      grpc_resolver *resolver);
 
 /** Get the next client config. Called by the channel to fetch a new
-    configuration. Expected to set *target_config with a new configuration,
+    configuration. Expected to set *result with a new configuration,
     and then schedule on_complete for execution.
 
-    If resolution is fatally broken, set *target_config to NULL and
+    If resolution is fatally broken, set *result to NULL and
     schedule on_complete. */
 void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver,
-                        grpc_client_config **target_config,
+                        grpc_resolver_result **result,
                         grpc_closure *on_complete);
 
 #endif /* GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_H */
diff --git a/src/core/ext/client_config/resolver_factory.h b/src/core/ext/client_config/resolver_factory.h
index 4eb6979..9ec5b9a 100644
--- a/src/core/ext/client_config/resolver_factory.h
+++ b/src/core/ext/client_config/resolver_factory.h
@@ -41,16 +41,13 @@
 typedef struct grpc_resolver_factory grpc_resolver_factory;
 typedef struct grpc_resolver_factory_vtable grpc_resolver_factory_vtable;
 
-/** grpc_resolver provides grpc_client_config objects to grpc_channel
+/** grpc_resolver provides grpc_resolver_result objects to grpc_channel
     objects */
 struct grpc_resolver_factory {
   const grpc_resolver_factory_vtable *vtable;
 };
 
-typedef struct grpc_resolver_args {
-  grpc_uri *uri;
-  grpc_client_channel_factory *client_channel_factory;
-} grpc_resolver_args;
+typedef struct grpc_resolver_args { grpc_uri *uri; } grpc_resolver_args;
 
 struct grpc_resolver_factory_vtable {
   void (*ref)(grpc_resolver_factory *factory);
diff --git a/src/core/ext/client_config/resolver_registry.c b/src/core/ext/client_config/resolver_registry.c
index e7a4abd..bd5c683 100644
--- a/src/core/ext/client_config/resolver_registry.c
+++ b/src/core/ext/client_config/resolver_registry.c
@@ -40,22 +40,20 @@
 #include <grpc/support/string_util.h>
 
 #define MAX_RESOLVERS 10
+#define DEFAULT_RESOLVER_PREFIX_MAX_LENGTH 32
 
 static grpc_resolver_factory *g_all_of_the_resolvers[MAX_RESOLVERS];
 static int g_number_of_resolvers = 0;
 
-static char *g_default_resolver_prefix;
+static char g_default_resolver_prefix[DEFAULT_RESOLVER_PREFIX_MAX_LENGTH] =
+    "dns:///";
 
-void grpc_resolver_registry_init(const char *default_resolver_prefix) {
-  g_default_resolver_prefix = gpr_strdup(default_resolver_prefix);
-}
+void grpc_resolver_registry_init() {}
 
 void grpc_resolver_registry_shutdown(void) {
-  int i;
-  for (i = 0; i < g_number_of_resolvers; i++) {
+  for (int i = 0; i < g_number_of_resolvers; i++) {
     grpc_resolver_factory_unref(g_all_of_the_resolvers[i]);
   }
-  gpr_free(g_default_resolver_prefix);
   // FIXME(ctiller): this should live in grpc_resolver_registry_init,
   // however that would have the client_config plugin call this AFTER we start
   // registering resolvers from third party plugins, and so they'd never show
@@ -65,6 +63,17 @@
   g_number_of_resolvers = 0;
 }
 
+void grpc_resolver_registry_set_default_prefix(
+    const char *default_resolver_prefix) {
+  const size_t len = strlen(default_resolver_prefix);
+  GPR_ASSERT(len < DEFAULT_RESOLVER_PREFIX_MAX_LENGTH &&
+             "default resolver prefix too long");
+  GPR_ASSERT(len > 0 && "default resolver prefix can't be empty");
+  // By the previous assert, default_resolver_prefix is safe to be copied with a
+  // plain strcpy.
+  strcpy(g_default_resolver_prefix, default_resolver_prefix);
+}
+
 void grpc_register_resolver_type(grpc_resolver_factory *factory) {
   int i;
   for (i = 0; i < g_number_of_resolvers; i++) {
@@ -108,35 +117,27 @@
   *uri = grpc_uri_parse(target, 1);
   factory = lookup_factory_by_uri(*uri);
   if (factory == NULL) {
-    if (g_default_resolver_prefix != NULL) {
-      grpc_uri_destroy(*uri);
-      gpr_asprintf(&tmp, "%s%s", g_default_resolver_prefix, target);
-      *uri = grpc_uri_parse(tmp, 1);
-      factory = lookup_factory_by_uri(*uri);
-      if (factory == NULL) {
-        grpc_uri_destroy(grpc_uri_parse(target, 0));
-        grpc_uri_destroy(grpc_uri_parse(tmp, 0));
-        gpr_log(GPR_ERROR, "don't know how to resolve '%s' or '%s'", target,
-                tmp);
-      }
-      gpr_free(tmp);
-    } else {
+    grpc_uri_destroy(*uri);
+    gpr_asprintf(&tmp, "%s%s", g_default_resolver_prefix, target);
+    *uri = grpc_uri_parse(tmp, 1);
+    factory = lookup_factory_by_uri(*uri);
+    if (factory == NULL) {
       grpc_uri_destroy(grpc_uri_parse(target, 0));
-      gpr_log(GPR_ERROR, "don't know how to resolve '%s'", target);
+      grpc_uri_destroy(grpc_uri_parse(tmp, 0));
+      gpr_log(GPR_ERROR, "don't know how to resolve '%s' or '%s'", target, tmp);
     }
+    gpr_free(tmp);
   }
   return factory;
 }
 
-grpc_resolver *grpc_resolver_create(
-    const char *target, grpc_client_channel_factory *client_channel_factory) {
+grpc_resolver *grpc_resolver_create(const char *target) {
   grpc_uri *uri = NULL;
   grpc_resolver_factory *factory = resolve_factory(target, &uri);
   grpc_resolver *resolver;
   grpc_resolver_args args;
   memset(&args, 0, sizeof(args));
   args.uri = uri;
-  args.client_channel_factory = client_channel_factory;
   resolver = grpc_resolver_factory_create_resolver(factory, &args);
   grpc_uri_destroy(uri);
   return resolver;
diff --git a/src/core/ext/client_config/resolver_registry.h b/src/core/ext/client_config/resolver_registry.h
index 5ef1383..4c6279b 100644
--- a/src/core/ext/client_config/resolver_registry.h
+++ b/src/core/ext/client_config/resolver_registry.h
@@ -36,9 +36,12 @@
 
 #include "src/core/ext/client_config/resolver_factory.h"
 
-void grpc_resolver_registry_init(const char *default_prefix);
+void grpc_resolver_registry_init();
 void grpc_resolver_registry_shutdown(void);
 
+/** Set the default URI prefix to \a default_prefix. */
+void grpc_resolver_registry_set_default_prefix(const char *default_prefix);
+
 /** Register a resolver type.
     URI's of \a scheme will be resolved with the given resolver.
     If \a priority is greater than zero, then the resolver will be eligible
@@ -55,8 +58,7 @@
     If a resolver factory was found, use it to instantiate a resolver and
     return it.
     If a resolver factory was not found, return NULL. */
-grpc_resolver *grpc_resolver_create(
-    const char *target, grpc_client_channel_factory *client_channel_factory);
+grpc_resolver *grpc_resolver_create(const char *target);
 
 /** Find a resolver factory given a name and return an (owned-by-the-caller)
  *  reference to it */
diff --git a/src/core/ext/client_config/resolver_result.c b/src/core/ext/client_config/resolver_result.c
new file mode 100644
index 0000000..63480d1
--- /dev/null
+++ b/src/core/ext/client_config/resolver_result.c
@@ -0,0 +1,94 @@
+//
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "src/core/ext/client_config/resolver_result.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/channel/channel_args.h"
+
+struct grpc_resolver_result {
+  gpr_refcount refs;
+  char* server_name;
+  grpc_lb_addresses* addresses;
+  char* lb_policy_name;
+  grpc_channel_args* lb_policy_args;
+};
+
+grpc_resolver_result* grpc_resolver_result_create(
+    const char* server_name, grpc_lb_addresses* addresses,
+    const char* lb_policy_name, grpc_channel_args* lb_policy_args) {
+  grpc_resolver_result* result = gpr_malloc(sizeof(*result));
+  memset(result, 0, sizeof(*result));
+  gpr_ref_init(&result->refs, 1);
+  result->server_name = gpr_strdup(server_name);
+  result->addresses = addresses;
+  result->lb_policy_name = gpr_strdup(lb_policy_name);
+  result->lb_policy_args = lb_policy_args;
+  return result;
+}
+
+void grpc_resolver_result_ref(grpc_resolver_result* result) {
+  gpr_ref(&result->refs);
+}
+
+void grpc_resolver_result_unref(grpc_exec_ctx* exec_ctx,
+                                grpc_resolver_result* result) {
+  if (gpr_unref(&result->refs)) {
+    gpr_free(result->server_name);
+    grpc_lb_addresses_destroy(result->addresses, NULL /* user_data_destroy */);
+    gpr_free(result->lb_policy_name);
+    grpc_channel_args_destroy(result->lb_policy_args);
+    gpr_free(result);
+  }
+}
+
+const char* grpc_resolver_result_get_server_name(grpc_resolver_result* result) {
+  return result->server_name;
+}
+
+grpc_lb_addresses* grpc_resolver_result_get_addresses(
+    grpc_resolver_result* result) {
+  return result->addresses;
+}
+
+const char* grpc_resolver_result_get_lb_policy_name(
+    grpc_resolver_result* result) {
+  return result->lb_policy_name;
+}
+
+grpc_channel_args* grpc_resolver_result_get_lb_policy_args(
+    grpc_resolver_result* result) {
+  return result->lb_policy_args;
+}
diff --git a/src/core/ext/client_config/resolver_result.h b/src/core/ext/client_config/resolver_result.h
new file mode 100644
index 0000000..414c2e2
--- /dev/null
+++ b/src/core/ext/client_config/resolver_result.h
@@ -0,0 +1,74 @@
+//
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_RESULT_H
+#define GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_RESULT_H
+
+#include "src/core/ext/client_config/lb_policy_factory.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+
+// TODO(roth, ctiller): In the long term, we are considering replacing
+// the resolver_result data structure with grpc_channel_args.  The idea is
+// that the resolver will return a set of channel args that contains the
+// information that is currently in the resolver_result struct.  For
+// example, there will be specific args indicating the set of addresses
+// and the name of the LB policy to instantiate.  Note that if we did
+// this, we would probably want to change the data structure of
+// grpc_channel_args such to a hash table or AVL or some other data
+// structure that does not require linear search to find keys.
+
+/// Results reported from a grpc_resolver.
+typedef struct grpc_resolver_result grpc_resolver_result;
+
+/// Takes ownership of \a addresses and \a lb_policy_args.
+grpc_resolver_result* grpc_resolver_result_create(
+    const char* server_name, grpc_lb_addresses* addresses,
+    const char* lb_policy_name, grpc_channel_args* lb_policy_args);
+void grpc_resolver_result_ref(grpc_resolver_result* result);
+void grpc_resolver_result_unref(grpc_exec_ctx* exec_ctx,
+                                grpc_resolver_result* result);
+
+/// Caller does NOT take ownership of result.
+const char* grpc_resolver_result_get_server_name(grpc_resolver_result* result);
+
+/// Caller does NOT take ownership of result.
+grpc_lb_addresses* grpc_resolver_result_get_addresses(
+    grpc_resolver_result* result);
+
+/// Caller does NOT take ownership of result.
+const char* grpc_resolver_result_get_lb_policy_name(
+    grpc_resolver_result* result);
+
+/// Caller does NOT take ownership of result.
+grpc_channel_args* grpc_resolver_result_get_lb_policy_args(
+    grpc_resolver_result* result);
+
+#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_RESULT_H */
diff --git a/src/core/ext/client_config/subchannel.c b/src/core/ext/client_config/subchannel.c
index d089cd4..0bbaa3e 100644
--- a/src/core/ext/client_config/subchannel.c
+++ b/src/core/ext/client_config/subchannel.c
@@ -33,6 +33,7 @@
 
 #include "src/core/ext/client_config/subchannel.h"
 
+#include <limits.h>
 #include <string.h>
 
 #include <grpc/support/alloc.h>
@@ -219,8 +220,8 @@
                             : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta);
 #ifdef GRPC_STREAM_REFCOUNT_DEBUG
   gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-          "SUBCHANNEL: %p % 12s 0x%08x -> 0x%08x [%s]", c, purpose, old_val,
-          old_val + delta, reason);
+          "SUBCHANNEL: %p %12s 0x%08d -> 0x%08d [%s]", c, purpose, (int)old_val,
+          (int)(old_val + delta), reason);
 #endif
   return old_val;
 }
@@ -331,41 +332,40 @@
   grpc_closure_init(&c->connected, subchannel_connected, c);
   grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE,
                                "subchannel");
-  gpr_backoff_init(&c->backoff_state,
-                   GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER,
-                   GRPC_SUBCHANNEL_RECONNECT_JITTER,
-                   GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000,
-                   GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
+  int initial_backoff_ms =
+      GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000;
+  int max_backoff_ms = GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000;
+  bool fixed_reconnect_backoff = false;
   if (c->args) {
     for (size_t i = 0; i < c->args->num_args; i++) {
       if (0 == strcmp(c->args->args[i].key,
                       "grpc.testing.fixed_reconnect_backoff")) {
         GPR_ASSERT(c->args->args[i].type == GRPC_ARG_INTEGER);
-        gpr_backoff_init(&c->backoff_state, 1.0, 0.0,
-                         c->args->args[i].value.integer,
-                         c->args->args[i].value.integer);
-      }
-      if (0 ==
-          strcmp(c->args->args[i].key, GRPC_ARG_MAX_RECONNECT_BACKOFF_MS)) {
-        if (c->args->args[i].type == GRPC_ARG_INTEGER) {
-          if (c->args->args[i].value.integer >= 0) {
-            gpr_backoff_init(
-                &c->backoff_state, GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER,
-                GRPC_SUBCHANNEL_RECONNECT_JITTER,
-                GPR_MIN(c->args->args[i].value.integer,
-                        GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000),
-                c->args->args[i].value.integer);
-          } else {
-            gpr_log(GPR_ERROR, GRPC_ARG_MAX_RECONNECT_BACKOFF_MS
-                    " : must be non-negative");
-          }
-        } else {
-          gpr_log(GPR_ERROR,
-                  GRPC_ARG_MAX_RECONNECT_BACKOFF_MS " : must be an integer");
-        }
+        fixed_reconnect_backoff = true;
+        initial_backoff_ms = max_backoff_ms = grpc_channel_arg_get_integer(
+            &c->args->args[i],
+            (grpc_integer_options){initial_backoff_ms, 100, INT_MAX});
+      } else if (0 == strcmp(c->args->args[i].key,
+                             GRPC_ARG_MAX_RECONNECT_BACKOFF_MS)) {
+        fixed_reconnect_backoff = false;
+        max_backoff_ms = grpc_channel_arg_get_integer(
+            &c->args->args[i],
+            (grpc_integer_options){max_backoff_ms, 100, INT_MAX});
+      } else if (0 == strcmp(c->args->args[i].key,
+                             GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS)) {
+        fixed_reconnect_backoff = false;
+        initial_backoff_ms = grpc_channel_arg_get_integer(
+            &c->args->args[i],
+            (grpc_integer_options){initial_backoff_ms, 100, INT_MAX});
       }
     }
   }
+  gpr_backoff_init(
+      &c->backoff_state,
+      fixed_reconnect_backoff ? 1.0
+                              : GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER,
+      fixed_reconnect_backoff ? 0.0 : GRPC_SUBCHANNEL_RECONNECT_JITTER,
+      initial_backoff_ms, max_backoff_ms);
   gpr_mu_init(&c->mu);
 
   return grpc_subchannel_index_register(exec_ctx, key, c);
@@ -504,14 +504,13 @@
                                           grpc_pollset_set *interested_parties,
                                           grpc_connectivity_state *state,
                                           grpc_closure *closure) {
-  grpc_transport_op op;
+  grpc_transport_op *op = grpc_make_transport_op(NULL);
   grpc_channel_element *elem;
-  memset(&op, 0, sizeof(op));
-  op.connectivity_state = state;
-  op.on_connectivity_state_change = closure;
-  op.bind_pollset_set = interested_parties;
+  op->connectivity_state = state;
+  op->on_connectivity_state_change = closure;
+  op->bind_pollset_set = interested_parties;
   elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(con), 0);
-  elem->filter->start_transport_op(exec_ctx, elem, &op);
+  elem->filter->start_transport_op(exec_ctx, elem, op);
 }
 
 void grpc_connected_subchannel_notify_on_state_change(
@@ -525,12 +524,11 @@
 void grpc_connected_subchannel_ping(grpc_exec_ctx *exec_ctx,
                                     grpc_connected_subchannel *con,
                                     grpc_closure *closure) {
-  grpc_transport_op op;
+  grpc_transport_op *op = grpc_make_transport_op(NULL);
   grpc_channel_element *elem;
-  memset(&op, 0, sizeof(op));
-  op.send_ping = closure;
+  op->send_ping = closure;
   elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(con), 0);
-  elem->filter->start_transport_op(exec_ctx, elem, &op);
+  elem->filter->start_transport_op(exec_ctx, elem, op);
 }
 
 static void publish_transport_locked(grpc_exec_ctx *exec_ctx,
@@ -635,7 +633,9 @@
     c->have_alarm = 1;
     grpc_connectivity_state_set(
         exec_ctx, &c->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
-        GRPC_ERROR_CREATE_REFERENCING("Connect Failed", &error, 1),
+        grpc_error_set_int(
+            GRPC_ERROR_CREATE_REFERENCING("Connect Failed", &error, 1),
+            GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE),
         "connect_failed");
     gpr_timespec time_til_next = gpr_time_sub(c->next_attempt, now);
     const char *errmsg = grpc_error_string(error);
@@ -702,19 +702,27 @@
   return GET_CONNECTED_SUBCHANNEL(c, acq);
 }
 
-grpc_subchannel_call *grpc_connected_subchannel_create_call(
+grpc_error *grpc_connected_subchannel_create_call(
     grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con,
-    grpc_polling_entity *pollent) {
+    grpc_polling_entity *pollent, gpr_timespec deadline,
+    grpc_subchannel_call **call) {
   grpc_channel_stack *chanstk = CHANNEL_STACK_FROM_CONNECTION(con);
-  grpc_subchannel_call *call =
-      gpr_malloc(sizeof(grpc_subchannel_call) + chanstk->call_stack_size);
-  grpc_call_stack *callstk = SUBCHANNEL_CALL_TO_CALL_STACK(call);
-  call->connection = con;
+  *call = gpr_malloc(sizeof(grpc_subchannel_call) + chanstk->call_stack_size);
+  grpc_call_stack *callstk = SUBCHANNEL_CALL_TO_CALL_STACK(*call);
+  (*call)->connection = con;  // Ref is added below.
+  grpc_error *error =
+      grpc_call_stack_init(exec_ctx, chanstk, 1, subchannel_call_destroy, *call,
+                           NULL, NULL, deadline, callstk);
+  if (error != GRPC_ERROR_NONE) {
+    const char *error_string = grpc_error_string(error);
+    gpr_log(GPR_ERROR, "error: %s", error_string);
+    grpc_error_free_string(error_string);
+    gpr_free(*call);
+    return error;
+  }
   GRPC_CONNECTED_SUBCHANNEL_REF(con, "subchannel_call");
-  grpc_call_stack_init(exec_ctx, chanstk, 1, subchannel_call_destroy, call,
-                       NULL, NULL, callstk);
   grpc_call_stack_set_pollset_or_pollset_set(exec_ctx, callstk, pollent);
-  return call;
+  return GRPC_ERROR_NONE;
 }
 
 grpc_call_stack *grpc_subchannel_call_get_call_stack(
diff --git a/src/core/ext/client_config/subchannel.h b/src/core/ext/client_config/subchannel.h
index b6d39f5..3330621 100644
--- a/src/core/ext/client_config/subchannel.h
+++ b/src/core/ext/client_config/subchannel.h
@@ -108,9 +108,10 @@
                                     GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
 
 /** construct a subchannel call */
-grpc_subchannel_call *grpc_connected_subchannel_create_call(
+grpc_error *grpc_connected_subchannel_create_call(
     grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *connected_subchannel,
-    grpc_polling_entity *pollent);
+    grpc_polling_entity *pollent, gpr_timespec deadline,
+    grpc_subchannel_call **subchannel_call);
 
 /** process a transport level op */
 void grpc_connected_subchannel_process_transport_op(
@@ -162,6 +163,8 @@
   size_t filter_count;
   /** Channel arguments to be supplied to the newly created channel */
   const grpc_channel_args *args;
+  /** Server name */
+  const char *server_name;
   /** Address to connect to */
   struct sockaddr *addr;
   size_t addr_len;
diff --git a/src/core/ext/client_config/subchannel_call_holder.c b/src/core/ext/client_config/subchannel_call_holder.c
deleted file mode 100644
index b96a0ad..0000000
--- a/src/core/ext/client_config/subchannel_call_holder.c
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include "src/core/ext/client_config/subchannel_call_holder.h"
-
-#include <grpc/support/alloc.h>
-
-#include "src/core/lib/profiling/timers.h"
-
-#define GET_CALL(holder) \
-  ((grpc_subchannel_call *)(gpr_atm_acq_load(&(holder)->subchannel_call)))
-
-#define CANCELLED_CALL ((grpc_subchannel_call *)1)
-
-static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *holder,
-                             grpc_error *error);
-static void retry_ops(grpc_exec_ctx *exec_ctx, void *retry_ops_args,
-                      grpc_error *error);
-
-static void add_waiting_locked(grpc_subchannel_call_holder *holder,
-                               grpc_transport_stream_op *op);
-static void fail_locked(grpc_exec_ctx *exec_ctx,
-                        grpc_subchannel_call_holder *holder, grpc_error *error);
-static void retry_waiting_locked(grpc_exec_ctx *exec_ctx,
-                                 grpc_subchannel_call_holder *holder);
-
-void grpc_subchannel_call_holder_init(
-    grpc_subchannel_call_holder *holder,
-    grpc_subchannel_call_holder_pick_subchannel pick_subchannel,
-    void *pick_subchannel_arg, grpc_call_stack *owning_call) {
-  gpr_atm_rel_store(&holder->subchannel_call, 0);
-  holder->pick_subchannel = pick_subchannel;
-  holder->pick_subchannel_arg = pick_subchannel_arg;
-  gpr_mu_init(&holder->mu);
-  holder->connected_subchannel = NULL;
-  holder->waiting_ops = NULL;
-  holder->waiting_ops_count = 0;
-  holder->waiting_ops_capacity = 0;
-  holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
-  holder->owning_call = owning_call;
-  holder->pollent = NULL;
-}
-
-void grpc_subchannel_call_holder_destroy(grpc_exec_ctx *exec_ctx,
-                                         grpc_subchannel_call_holder *holder) {
-  grpc_subchannel_call *call = GET_CALL(holder);
-  if (call != NULL && call != CANCELLED_CALL) {
-    GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, call, "holder");
-  }
-  GPR_ASSERT(holder->creation_phase ==
-             GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING);
-  gpr_mu_destroy(&holder->mu);
-  GPR_ASSERT(holder->waiting_ops_count == 0);
-  gpr_free(holder->waiting_ops);
-}
-
-void grpc_subchannel_call_holder_perform_op(grpc_exec_ctx *exec_ctx,
-                                            grpc_subchannel_call_holder *holder,
-                                            grpc_transport_stream_op *op) {
-  /* try to (atomically) get the call */
-  grpc_subchannel_call *call = GET_CALL(holder);
-  GPR_TIMER_BEGIN("grpc_subchannel_call_holder_perform_op", 0);
-  if (call == CANCELLED_CALL) {
-    grpc_transport_stream_op_finish_with_failure(exec_ctx, op,
-                                                 GRPC_ERROR_CANCELLED);
-    GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0);
-    return;
-  }
-  if (call != NULL) {
-    grpc_subchannel_call_process_op(exec_ctx, call, op);
-    GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0);
-    return;
-  }
-  /* we failed; lock and figure out what to do */
-  gpr_mu_lock(&holder->mu);
-retry:
-  /* need to recheck that another thread hasn't set the call */
-  call = GET_CALL(holder);
-  if (call == CANCELLED_CALL) {
-    gpr_mu_unlock(&holder->mu);
-    grpc_transport_stream_op_finish_with_failure(exec_ctx, op,
-                                                 GRPC_ERROR_CANCELLED);
-    GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0);
-    return;
-  }
-  if (call != NULL) {
-    gpr_mu_unlock(&holder->mu);
-    grpc_subchannel_call_process_op(exec_ctx, call, op);
-    GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0);
-    return;
-  }
-  /* if this is a cancellation, then we can raise our cancelled flag */
-  if (op->cancel_error != GRPC_ERROR_NONE) {
-    if (!gpr_atm_rel_cas(&holder->subchannel_call, 0, 1)) {
-      goto retry;
-    } else {
-      switch (holder->creation_phase) {
-        case GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING:
-          fail_locked(exec_ctx, holder, GRPC_ERROR_REF(op->cancel_error));
-          break;
-        case GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL:
-          holder->pick_subchannel(exec_ctx, holder->pick_subchannel_arg, NULL,
-                                  0, &holder->connected_subchannel, NULL);
-          break;
-      }
-      gpr_mu_unlock(&holder->mu);
-      grpc_transport_stream_op_finish_with_failure(exec_ctx, op,
-                                                   GRPC_ERROR_CANCELLED);
-      GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0);
-      return;
-    }
-  }
-  /* if we don't have a subchannel, try to get one */
-  if (holder->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING &&
-      holder->connected_subchannel == NULL &&
-      op->send_initial_metadata != NULL) {
-    holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL;
-    grpc_closure_init(&holder->next_step, subchannel_ready, holder);
-    GRPC_CALL_STACK_REF(holder->owning_call, "pick_subchannel");
-    if (holder->pick_subchannel(
-            exec_ctx, holder->pick_subchannel_arg, op->send_initial_metadata,
-            op->send_initial_metadata_flags, &holder->connected_subchannel,
-            &holder->next_step)) {
-      holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
-      GRPC_CALL_STACK_UNREF(exec_ctx, holder->owning_call, "pick_subchannel");
-    }
-  }
-  /* if we've got a subchannel, then let's ask it to create a call */
-  if (holder->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING &&
-      holder->connected_subchannel != NULL) {
-    gpr_atm_rel_store(
-        &holder->subchannel_call,
-        (gpr_atm)(uintptr_t)grpc_connected_subchannel_create_call(
-            exec_ctx, holder->connected_subchannel, holder->pollent));
-    retry_waiting_locked(exec_ctx, holder);
-    goto retry;
-  }
-  /* nothing to be done but wait */
-  add_waiting_locked(holder, op);
-  gpr_mu_unlock(&holder->mu);
-  GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0);
-}
-
-static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg,
-                             grpc_error *error) {
-  grpc_subchannel_call_holder *holder = arg;
-  gpr_mu_lock(&holder->mu);
-  GPR_ASSERT(holder->creation_phase ==
-             GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL);
-  holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
-  if (holder->connected_subchannel == NULL) {
-    gpr_atm_no_barrier_store(&holder->subchannel_call, 1);
-    fail_locked(exec_ctx, holder,
-                GRPC_ERROR_CREATE_REFERENCING("Failed to create subchannel",
-                                              &error, 1));
-  } else if (1 == gpr_atm_acq_load(&holder->subchannel_call)) {
-    /* already cancelled before subchannel became ready */
-    fail_locked(exec_ctx, holder,
-                GRPC_ERROR_CREATE_REFERENCING(
-                    "Cancelled before creating subchannel", &error, 1));
-  } else {
-    gpr_atm_rel_store(
-        &holder->subchannel_call,
-        (gpr_atm)(uintptr_t)grpc_connected_subchannel_create_call(
-            exec_ctx, holder->connected_subchannel, holder->pollent));
-    retry_waiting_locked(exec_ctx, holder);
-  }
-  gpr_mu_unlock(&holder->mu);
-  GRPC_CALL_STACK_UNREF(exec_ctx, holder->owning_call, "pick_subchannel");
-}
-
-typedef struct {
-  grpc_transport_stream_op *ops;
-  size_t nops;
-  grpc_subchannel_call *call;
-} retry_ops_args;
-
-static void retry_waiting_locked(grpc_exec_ctx *exec_ctx,
-                                 grpc_subchannel_call_holder *holder) {
-  retry_ops_args *a = gpr_malloc(sizeof(*a));
-  a->ops = holder->waiting_ops;
-  a->nops = holder->waiting_ops_count;
-  a->call = GET_CALL(holder);
-  if (a->call == CANCELLED_CALL) {
-    gpr_free(a);
-    fail_locked(exec_ctx, holder, GRPC_ERROR_CANCELLED);
-    return;
-  }
-  holder->waiting_ops = NULL;
-  holder->waiting_ops_count = 0;
-  holder->waiting_ops_capacity = 0;
-  GRPC_SUBCHANNEL_CALL_REF(a->call, "retry_ops");
-  grpc_exec_ctx_sched(exec_ctx, grpc_closure_create(retry_ops, a),
-                      GRPC_ERROR_NONE, NULL);
-}
-
-static void retry_ops(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) {
-  retry_ops_args *a = args;
-  size_t i;
-  for (i = 0; i < a->nops; i++) {
-    grpc_subchannel_call_process_op(exec_ctx, a->call, &a->ops[i]);
-  }
-  GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, a->call, "retry_ops");
-  gpr_free(a->ops);
-  gpr_free(a);
-}
-
-static void add_waiting_locked(grpc_subchannel_call_holder *holder,
-                               grpc_transport_stream_op *op) {
-  GPR_TIMER_BEGIN("add_waiting_locked", 0);
-  if (holder->waiting_ops_count == holder->waiting_ops_capacity) {
-    holder->waiting_ops_capacity = GPR_MAX(3, 2 * holder->waiting_ops_capacity);
-    holder->waiting_ops =
-        gpr_realloc(holder->waiting_ops, holder->waiting_ops_capacity *
-                                             sizeof(*holder->waiting_ops));
-  }
-  holder->waiting_ops[holder->waiting_ops_count++] = *op;
-  GPR_TIMER_END("add_waiting_locked", 0);
-}
-
-static void fail_locked(grpc_exec_ctx *exec_ctx,
-                        grpc_subchannel_call_holder *holder,
-                        grpc_error *error) {
-  size_t i;
-  for (i = 0; i < holder->waiting_ops_count; i++) {
-    grpc_transport_stream_op_finish_with_failure(
-        exec_ctx, &holder->waiting_ops[i], GRPC_ERROR_REF(error));
-  }
-  holder->waiting_ops_count = 0;
-  GRPC_ERROR_UNREF(error);
-}
-
-char *grpc_subchannel_call_holder_get_peer(
-    grpc_exec_ctx *exec_ctx, grpc_subchannel_call_holder *holder) {
-  grpc_subchannel_call *subchannel_call = GET_CALL(holder);
-
-  if (subchannel_call == NULL || subchannel_call == CANCELLED_CALL) {
-    return NULL;
-  } else {
-    return grpc_subchannel_call_get_peer(exec_ctx, subchannel_call);
-  }
-}
diff --git a/src/core/ext/client_config/subchannel_call_holder.h b/src/core/ext/client_config/subchannel_call_holder.h
deleted file mode 100644
index 8d2deb0..0000000
--- a/src/core/ext/client_config/subchannel_call_holder.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_CALL_HOLDER_H
-#define GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_CALL_HOLDER_H
-
-#include "src/core/ext/client_config/subchannel.h"
-#include "src/core/lib/iomgr/polling_entity.h"
-
-/** Pick a subchannel for grpc_subchannel_call_holder;
-    Return 1 if subchannel is available immediately (in which case on_ready
-    should not be called), or 0 otherwise (in which case on_ready should be
-    called when the subchannel is available) */
-typedef int (*grpc_subchannel_call_holder_pick_subchannel)(
-    grpc_exec_ctx *exec_ctx, void *arg, grpc_metadata_batch *initial_metadata,
-    uint32_t initial_metadata_flags,
-    grpc_connected_subchannel **connected_subchannel, grpc_closure *on_ready);
-
-typedef enum {
-  GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING,
-  GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL
-} grpc_subchannel_call_holder_creation_phase;
-
-/** Wrapper for holding a pointer to grpc_subchannel_call, and the
-    associated machinery to create such a pointer.
-    Handles queueing of stream ops until a call object is ready, waiting
-    for initial metadata before trying to create a call object,
-    and handling cancellation gracefully.
-
-    The channel filter uses this as their call_data. */
-typedef struct grpc_subchannel_call_holder {
-  /** either 0 for no call, 1 for cancelled, or a pointer to a
-      grpc_subchannel_call */
-  gpr_atm subchannel_call;
-  /** Helper function to choose the subchannel on which to create
-      the call object. Channel filter delegates to the load
-      balancing policy (once it's ready). */
-  grpc_subchannel_call_holder_pick_subchannel pick_subchannel;
-  void *pick_subchannel_arg;
-
-  gpr_mu mu;
-
-  grpc_subchannel_call_holder_creation_phase creation_phase;
-  grpc_connected_subchannel *connected_subchannel;
-  grpc_polling_entity *pollent;
-
-  grpc_transport_stream_op *waiting_ops;
-  size_t waiting_ops_count;
-  size_t waiting_ops_capacity;
-
-  grpc_closure next_step;
-
-  grpc_call_stack *owning_call;
-} grpc_subchannel_call_holder;
-
-void grpc_subchannel_call_holder_init(
-    grpc_subchannel_call_holder *holder,
-    grpc_subchannel_call_holder_pick_subchannel pick_subchannel,
-    void *pick_subchannel_arg, grpc_call_stack *owning_call);
-void grpc_subchannel_call_holder_destroy(grpc_exec_ctx *exec_ctx,
-                                         grpc_subchannel_call_holder *holder);
-
-void grpc_subchannel_call_holder_perform_op(grpc_exec_ctx *exec_ctx,
-                                            grpc_subchannel_call_holder *holder,
-                                            grpc_transport_stream_op *op);
-char *grpc_subchannel_call_holder_get_peer(grpc_exec_ctx *exec_ctx,
-                                           grpc_subchannel_call_holder *holder);
-
-#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_CALL_HOLDER_H */
diff --git a/src/core/ext/client_config/subchannel_factory.c b/src/core/ext/client_config/subchannel_factory.c
deleted file mode 100644
index d1e4d75..0000000
--- a/src/core/ext/client_config/subchannel_factory.c
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include "src/core/ext/client_config/subchannel_factory.h"
-
-void grpc_subchannel_factory_ref(grpc_subchannel_factory* factory) {
-  factory->vtable->ref(factory);
-}
-
-void grpc_subchannel_factory_unref(grpc_exec_ctx* exec_ctx,
-                                   grpc_subchannel_factory* factory) {
-  factory->vtable->unref(exec_ctx, factory);
-}
-
-grpc_subchannel* grpc_subchannel_factory_create_subchannel(
-    grpc_exec_ctx* exec_ctx, grpc_subchannel_factory* factory,
-    grpc_subchannel_args* args) {
-  return factory->vtable->create_subchannel(exec_ctx, factory, args);
-}
diff --git a/src/core/ext/client_config/subchannel_factory.h b/src/core/ext/client_config/subchannel_factory.h
deleted file mode 100644
index 0fb806d..0000000
--- a/src/core/ext/client_config/subchannel_factory.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H
-#define GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H
-
-#include "src/core/ext/client_config/subchannel.h"
-#include "src/core/lib/channel/channel_stack.h"
-
-typedef struct grpc_subchannel_factory grpc_subchannel_factory;
-typedef struct grpc_subchannel_factory_vtable grpc_subchannel_factory_vtable;
-
-/** Constructor for new configured channels.
-    Creating decorators around this type is encouraged to adapt behavior. */
-struct grpc_subchannel_factory {
-  const grpc_subchannel_factory_vtable *vtable;
-};
-
-struct grpc_subchannel_factory_vtable {
-  void (*ref)(grpc_subchannel_factory *factory);
-  void (*unref)(grpc_exec_ctx *exec_ctx, grpc_subchannel_factory *factory);
-  grpc_subchannel *(*create_subchannel)(grpc_exec_ctx *exec_ctx,
-                                        grpc_subchannel_factory *factory,
-                                        grpc_subchannel_args *args);
-};
-
-void grpc_subchannel_factory_ref(grpc_subchannel_factory *factory);
-void grpc_subchannel_factory_unref(grpc_exec_ctx *exec_ctx,
-                                   grpc_subchannel_factory *factory);
-
-/** Create a new grpc_subchannel */
-grpc_subchannel *grpc_subchannel_factory_create_subchannel(
-    grpc_exec_ctx *exec_ctx, grpc_subchannel_factory *factory,
-    grpc_subchannel_args *args);
-
-#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H */
diff --git a/src/core/ext/client_config/subchannel_index.c b/src/core/ext/client_config/subchannel_index.c
index 690cb16..673f85b 100644
--- a/src/core/ext/client_config/subchannel_index.c
+++ b/src/core/ext/client_config/subchannel_index.c
@@ -38,6 +38,7 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/avl.h>
+#include <grpc/support/string_util.h>
 #include <grpc/support/tls.h>
 
 #include "src/core/lib/channel/channel_args.h"
@@ -85,6 +86,7 @@
   } else {
     k->args.filters = NULL;
   }
+  k->args.server_name = gpr_strdup(args->server_name);
   k->args.addr_len = args->addr_len;
   k->args.addr = gpr_malloc(args->addr_len);
   if (k->args.addr_len > 0) {
@@ -111,6 +113,8 @@
   if (c != 0) return c;
   c = GPR_ICMP(a->args.filter_count, b->args.filter_count);
   if (c != 0) return c;
+  c = strcmp(a->args.server_name, b->args.server_name);
+  if (c != 0) return c;
   if (a->args.addr_len) {
     c = memcmp(a->args.addr, b->args.addr, a->args.addr_len);
     if (c != 0) return c;
@@ -126,9 +130,10 @@
 void grpc_subchannel_key_destroy(grpc_exec_ctx *exec_ctx,
                                  grpc_subchannel_key *k) {
   grpc_connector_unref(exec_ctx, k->connector);
-  gpr_free(k->args.addr);
   gpr_free((grpc_channel_args *)k->args.filters);
   grpc_channel_args_destroy((grpc_channel_args *)k->args.args);
+  gpr_free((void *)k->args.server_name);
+  gpr_free(k->args.addr);
   gpr_free(k);
 }
 
diff --git a/src/core/ext/lb_policy/grpclb/grpclb.c b/src/core/ext/lb_policy/grpclb/grpclb.c
new file mode 100644
index 0000000..63af774
--- /dev/null
+++ b/src/core/ext/lb_policy/grpclb/grpclb.c
@@ -0,0 +1,1192 @@
+/*
+ *
+ * Copyright 2016, 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.
+ *
+ */
+
+/** Implementation of the gRPC LB policy.
+ *
+ * This policy takes as input a set of resolved addresses {a1..an} for which the
+ * LB set was set (it's the resolver's responsibility to ensure this). That is
+ * to say, {a1..an} represent a collection of LB servers.
+ *
+ * An internal channel (\a glb_lb_policy.lb_channel) is created over {a1..an}.
+ * This channel behaves just like a regular channel. In particular, the
+ * constructed URI over the addresses a1..an will use the default pick first
+ * policy to select from this list of LB server backends.
+ *
+ * The first time the policy gets a request for a pick, a ping, or to exit the
+ * idle state, \a query_for_backends() is called. It creates an instance of \a
+ * lb_client_data, an internal struct meant to contain the data associated with
+ * the internal communication with the LB server. This instance is created via
+ * \a lb_client_data_create(). There, the call over lb_channel to pick-first
+ * from {a1..an} is created, the \a LoadBalancingRequest message is assembled
+ * and all necessary callbacks for the progress of the internal call configured.
+ *
+ * Back in \a query_for_backends(), the internal *streaming* call to the LB
+ * server (whichever address from {a1..an} pick-first chose) is kicked off.
+ * It'll progress over the callbacks configured in \a lb_client_data_create()
+ * (see the field docstrings of \a lb_client_data for more details).
+ *
+ * If the call fails with UNIMPLEMENTED, the original call will also fail.
+ * There's a misconfiguration somewhere: at least one of {a1..an} isn't a LB
+ * server, which contradicts the LB bit being set. If the internal call times
+ * out, the usual behavior of pick-first applies, continuing to pick from the
+ * list {a1..an}.
+ *
+ * Upon sucesss, a \a LoadBalancingResponse is expected in \a res_recv_cb. An
+ * invalid one results in the termination of the streaming call. A new streaming
+ * call should be created if possible, failing the original call otherwise.
+ * For a valid \a LoadBalancingResponse, the server list of actual backends is
+ * extracted. A Round Robin policy will be created from this list. There are two
+ * possible scenarios:
+ *
+ * 1. This is the first server list received. There was no previous instance of
+ *    the Round Robin policy. \a rr_handover() will instantiate the RR policy
+ *    and perform all the pending operations over it.
+ * 2. There's already a RR policy instance active. We need to introduce the new
+ *    one build from the new serverlist, but taking care not to disrupt the
+ *    operations in progress over the old RR instance. This is done by
+ *    decreasing the reference count on the old policy. The moment no more
+ *    references are held on the old RR policy, it'll be destroyed and \a
+ *    glb_rr_connectivity_changed notified with a \a GRPC_CHANNEL_SHUTDOWN
+ *    state. At this point we can transition to a new RR instance safely, which
+ *    is done once again via \a rr_handover().
+ *
+ *
+ * Once a RR policy instance is in place (and getting updated as described),
+ * calls to for a pick, a ping or a cancellation will be serviced right away by
+ * forwarding them to the RR instance. Any time there's no RR policy available
+ * (ie, right after the creation of the gRPCLB policy, if an empty serverlist
+ * is received, etc), pick/ping requests are added to a list of pending
+ * picks/pings to be flushed and serviced as part of \a rr_handover() the moment
+ * the RR policy instance becomes available.
+ *
+ * \see https://github.com/grpc/grpc/blob/master/doc/load-balancing.md for the
+ * high level design and details. */
+
+/* TODO(dgq):
+ * - Implement LB service forwarding (point 2c. in the doc's diagram).
+ */
+
+#include <errno.h>
+
+#include <string.h>
+
+#include <grpc/byte_buffer_reader.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/ext/client_config/client_channel_factory.h"
+#include "src/core/ext/client_config/lb_policy_factory.h"
+#include "src/core/ext/client_config/lb_policy_registry.h"
+#include "src/core/ext/client_config/parse_address.h"
+#include "src/core/ext/lb_policy/grpclb/grpclb.h"
+#include "src/core/ext/lb_policy/grpclb/load_balancer_api.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+int grpc_lb_glb_trace = 0;
+
+/* add lb_token of selected subchannel (address) to the call's initial
+ * metadata */
+static void initial_metadata_add_lb_token(
+    grpc_metadata_batch *initial_metadata,
+    grpc_linked_mdelem *lb_token_mdelem_storage, grpc_mdelem *lb_token) {
+  GPR_ASSERT(lb_token_mdelem_storage != NULL);
+  GPR_ASSERT(lb_token != NULL);
+  grpc_metadata_batch_add_tail(initial_metadata, lb_token_mdelem_storage,
+                               lb_token);
+}
+
+typedef struct wrapped_rr_closure_arg {
+  /* the original closure. Usually a on_complete/notify cb for pick() and ping()
+   * calls against the internal RR instance, respectively. */
+  grpc_closure *wrapped_closure;
+
+  /* the pick's initial metadata, kept in order to append the LB token for the
+   * pick */
+  grpc_metadata_batch *initial_metadata;
+
+  /* the picked target, used to determine which LB token to add to the pick's
+   * initial metadata */
+  grpc_connected_subchannel **target;
+
+  /* the LB token associated with the pick */
+  grpc_mdelem *lb_token;
+
+  /* storage for the lb token initial metadata mdelem */
+  grpc_linked_mdelem *lb_token_mdelem_storage;
+
+  /* The RR instance related to the closure */
+  grpc_lb_policy *rr_policy;
+
+  /* when not NULL, represents a pending_{pick,ping} node to be freed upon
+   * closure execution */
+  void *owning_pending_node; /* to be freed if not NULL */
+} wrapped_rr_closure_arg;
+
+/* The \a on_complete closure passed as part of the pick requires keeping a
+ * reference to its associated round robin instance. We wrap this closure in
+ * order to unref the round robin instance upon its invocation */
+static void wrapped_rr_closure(grpc_exec_ctx *exec_ctx, void *arg,
+                               grpc_error *error) {
+  wrapped_rr_closure_arg *wc_arg = arg;
+  if (wc_arg->rr_policy != NULL) {
+    if (grpc_lb_glb_trace) {
+      gpr_log(GPR_INFO, "Unreffing RR (0x%" PRIxPTR ")",
+              (intptr_t)wc_arg->rr_policy);
+    }
+    GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, "wrapped_rr_closure");
+
+    /* if target is NULL, no pick has been made by the RR policy (eg, all
+     * addresses failed to connect). There won't be any user_data/token
+     * available */
+    if (wc_arg->target != NULL) {
+      initial_metadata_add_lb_token(wc_arg->initial_metadata,
+                                    wc_arg->lb_token_mdelem_storage,
+                                    GRPC_MDELEM_REF(wc_arg->lb_token));
+    }
+  }
+  GPR_ASSERT(wc_arg->wrapped_closure != NULL);
+
+  grpc_exec_ctx_sched(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_REF(error),
+                      NULL);
+  gpr_free(wc_arg->owning_pending_node);
+}
+
+/* Linked list of pending pick requests. It stores all information needed to
+ * eventually call (Round Robin's) pick() on them. They mainly stay pending
+ * waiting for the RR policy to be created/updated.
+ *
+ * One particularity is the wrapping of the user-provided \a on_complete closure
+ * (in \a wrapped_on_complete and \a wrapped_on_complete_arg). This is needed in
+ * order to correctly unref the RR policy instance upon completion of the pick.
+ * See \a wrapped_rr_closure for details. */
+typedef struct pending_pick {
+  struct pending_pick *next;
+
+  /* original pick()'s arguments */
+  grpc_lb_policy_pick_args pick_args;
+
+  /* output argument where to store the pick()ed connected subchannel, or NULL
+   * upon error. */
+  grpc_connected_subchannel **target;
+
+  /* a closure wrapping the original on_complete one to be invoked once the
+   * pick() has completed (regardless of success) */
+  grpc_closure wrapped_on_complete;
+
+  /* args for wrapped_on_complete */
+  wrapped_rr_closure_arg wrapped_on_complete_arg;
+} pending_pick;
+
+static void add_pending_pick(pending_pick **root,
+                             const grpc_lb_policy_pick_args *pick_args,
+                             grpc_connected_subchannel **target,
+                             grpc_closure *on_complete) {
+  pending_pick *pp = gpr_malloc(sizeof(*pp));
+  memset(pp, 0, sizeof(pending_pick));
+  memset(&pp->wrapped_on_complete_arg, 0, sizeof(wrapped_rr_closure_arg));
+  pp->next = *root;
+  pp->pick_args = *pick_args;
+  pp->target = target;
+  pp->wrapped_on_complete_arg.wrapped_closure = on_complete;
+  pp->wrapped_on_complete_arg.target = target;
+  pp->wrapped_on_complete_arg.initial_metadata = pick_args->initial_metadata;
+  pp->wrapped_on_complete_arg.lb_token_mdelem_storage =
+      pick_args->lb_token_mdelem_storage;
+  grpc_closure_init(&pp->wrapped_on_complete, wrapped_rr_closure,
+                    &pp->wrapped_on_complete_arg);
+  *root = pp;
+}
+
+/* Same as the \a pending_pick struct but for ping operations */
+typedef struct pending_ping {
+  struct pending_ping *next;
+
+  /* a closure wrapping the original on_complete one to be invoked once the
+   * ping() has completed (regardless of success) */
+  grpc_closure wrapped_notify;
+
+  /* args for wrapped_notify */
+  wrapped_rr_closure_arg wrapped_notify_arg;
+} pending_ping;
+
+static void add_pending_ping(pending_ping **root, grpc_closure *notify) {
+  pending_ping *pping = gpr_malloc(sizeof(*pping));
+  memset(pping, 0, sizeof(pending_ping));
+  memset(&pping->wrapped_notify_arg, 0, sizeof(wrapped_rr_closure_arg));
+  pping->next = *root;
+  grpc_closure_init(&pping->wrapped_notify, wrapped_rr_closure,
+                    &pping->wrapped_notify_arg);
+  pping->wrapped_notify_arg.wrapped_closure = notify;
+  *root = pping;
+}
+
+/*
+ * glb_lb_policy
+ */
+typedef struct rr_connectivity_data rr_connectivity_data;
+struct lb_client_data;
+static const grpc_lb_policy_vtable glb_lb_policy_vtable;
+typedef struct glb_lb_policy {
+  /** base policy: must be first */
+  grpc_lb_policy base;
+
+  /** mutex protecting remaining members */
+  gpr_mu mu;
+
+  /** who the client is trying to communicate with */
+  const char *server_name;
+  grpc_client_channel_factory *cc_factory;
+
+  /** deadline for the LB's call */
+  gpr_timespec deadline;
+
+  /** for communicating with the LB server */
+  grpc_channel *lb_channel;
+
+  /** the RR policy to use of the backend servers returned by the LB server */
+  grpc_lb_policy *rr_policy;
+
+  bool started_picking;
+
+  /** our connectivity state tracker */
+  grpc_connectivity_state_tracker state_tracker;
+
+  /** stores the deserialized response from the LB. May be NULL until one such
+   * response has arrived. */
+  grpc_grpclb_serverlist *serverlist;
+
+  /** addresses from \a serverlist */
+  grpc_lb_addresses *addresses;
+
+  /** list of picks that are waiting on RR's policy connectivity */
+  pending_pick *pending_picks;
+
+  /** list of pings that are waiting on RR's policy connectivity */
+  pending_ping *pending_pings;
+
+  /** client data associated with the LB server communication */
+  struct lb_client_data *lb_client;
+
+  /** for tracking of the RR connectivity */
+  rr_connectivity_data *rr_connectivity;
+
+  /* a wrapped (see \a wrapped_rr_closure) on-complete closure for readily
+   * available RR picks */
+  grpc_closure wrapped_on_complete;
+
+  /* arguments for the wrapped_on_complete closure */
+  wrapped_rr_closure_arg wc_arg;
+} glb_lb_policy;
+
+/* Keeps track and reacts to changes in connectivity of the RR instance */
+struct rr_connectivity_data {
+  grpc_closure on_change;
+  grpc_connectivity_state state;
+  glb_lb_policy *glb_policy;
+};
+
+static bool is_server_valid(const grpc_grpclb_server *server, size_t idx,
+                            bool log) {
+  const grpc_grpclb_ip_address *ip = &server->ip_address;
+  if (server->port >> 16 != 0) {
+    if (log) {
+      gpr_log(GPR_ERROR,
+              "Invalid port '%d' at index %zu of serverlist. Ignoring.",
+              server->port, idx);
+    }
+    return false;
+  }
+
+  if (ip->size != 4 && ip->size != 16) {
+    if (log) {
+      gpr_log(GPR_ERROR,
+              "Expected IP to be 4 or 16 bytes, got %d at index %zu of "
+              "serverlist. Ignoring",
+              ip->size, idx);
+    }
+    return false;
+  }
+  return true;
+}
+
+/* Returns addresses extracted from \a serverlist. */
+static grpc_lb_addresses *process_serverlist(
+    const grpc_grpclb_serverlist *serverlist) {
+  size_t num_valid = 0;
+  /* first pass: count how many are valid in order to allocate the necessary
+   * memory in a single block */
+  for (size_t i = 0; i < serverlist->num_servers; ++i) {
+    if (is_server_valid(serverlist->servers[i], i, true)) ++num_valid;
+  }
+  if (num_valid == 0) return NULL;
+
+  grpc_lb_addresses *lb_addresses = grpc_lb_addresses_create(num_valid);
+
+  /* second pass: actually populate the addresses and LB tokens (aka user data
+   * to the outside world) to be read by the RR policy during its creation.
+   * Given that the validity tests are very cheap, they are performed again
+   * instead of marking the valid ones during the first pass, as this would
+   * incurr in an allocation due to the arbitrary number of server */
+  size_t addr_idx = 0;
+  for (size_t sl_idx = 0; sl_idx < serverlist->num_servers; ++sl_idx) {
+    GPR_ASSERT(addr_idx < num_valid);
+    const grpc_grpclb_server *server = serverlist->servers[sl_idx];
+    if (!is_server_valid(serverlist->servers[sl_idx], sl_idx, false)) continue;
+
+    /* address processing */
+    const uint16_t netorder_port = htons((uint16_t)server->port);
+    /* the addresses are given in binary format (a in(6)_addr struct) in
+     * server->ip_address.bytes. */
+    const grpc_grpclb_ip_address *ip = &server->ip_address;
+    grpc_resolved_address addr;
+    memset(&addr, 0, sizeof(addr));
+    if (ip->size == 4) {
+      addr.len = sizeof(struct sockaddr_in);
+      struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr.addr;
+      addr4->sin_family = AF_INET;
+      memcpy(&addr4->sin_addr, ip->bytes, ip->size);
+      addr4->sin_port = netorder_port;
+    } else if (ip->size == 16) {
+      addr.len = sizeof(struct sockaddr_in6);
+      struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr.addr;
+      addr6->sin6_family = AF_INET;
+      memcpy(&addr6->sin6_addr, ip->bytes, ip->size);
+      addr6->sin6_port = netorder_port;
+    }
+
+    /* lb token processing */
+    void *user_data;
+    if (server->has_load_balance_token) {
+      const size_t lb_token_size =
+          GPR_ARRAY_SIZE(server->load_balance_token) - 1;
+      grpc_mdstr *lb_token_mdstr = grpc_mdstr_from_buffer(
+          (uint8_t *)server->load_balance_token, lb_token_size);
+      user_data = grpc_mdelem_from_metadata_strings(
+          GRPC_MDSTR_LOAD_REPORTING_INITIAL, lb_token_mdstr);
+    } else {
+      gpr_log(GPR_ERROR,
+              "Missing LB token for backend address '%s'. The empty token will "
+              "be used instead",
+              grpc_sockaddr_to_uri((struct sockaddr *)&addr.addr));
+      user_data = GRPC_MDELEM_LOAD_REPORTING_INITIAL_EMPTY;
+    }
+
+    grpc_lb_addresses_set_address(lb_addresses, addr_idx, &addr.addr, addr.len,
+                                  false /* is_balancer */,
+                                  NULL /* balancer_name */, user_data);
+    ++addr_idx;
+  }
+  GPR_ASSERT(addr_idx == num_valid);
+
+  return lb_addresses;
+}
+
+/* A plugin for grpc_lb_addresses_destroy that unrefs the LB token metadata. */
+static void lb_token_destroy(void *token) {
+  if (token != NULL) GRPC_MDELEM_UNREF(token);
+}
+
+static grpc_lb_policy *create_rr(grpc_exec_ctx *exec_ctx,
+                                 const grpc_grpclb_serverlist *serverlist,
+                                 glb_lb_policy *glb_policy) {
+  GPR_ASSERT(serverlist != NULL && serverlist->num_servers > 0);
+
+  grpc_lb_policy_args args;
+  memset(&args, 0, sizeof(args));
+  args.server_name = glb_policy->server_name;
+  args.client_channel_factory = glb_policy->cc_factory;
+  args.addresses = process_serverlist(serverlist);
+
+  grpc_lb_policy *rr = grpc_lb_policy_create(exec_ctx, "round_robin", &args);
+
+  if (glb_policy->addresses != NULL) {
+    /* dispose of the previous version */
+    grpc_lb_addresses_destroy(glb_policy->addresses, lb_token_destroy);
+  }
+  glb_policy->addresses = args.addresses;
+
+  return rr;
+}
+
+static void rr_handover(grpc_exec_ctx *exec_ctx, glb_lb_policy *glb_policy,
+                        grpc_error *error) {
+  GPR_ASSERT(glb_policy->serverlist != NULL &&
+             glb_policy->serverlist->num_servers > 0);
+  glb_policy->rr_policy =
+      create_rr(exec_ctx, glb_policy->serverlist, glb_policy);
+
+  if (grpc_lb_glb_trace) {
+    gpr_log(GPR_INFO, "Created RR policy (0x%" PRIxPTR ")",
+            (intptr_t)glb_policy->rr_policy);
+  }
+  GPR_ASSERT(glb_policy->rr_policy != NULL);
+  glb_policy->rr_connectivity->state = grpc_lb_policy_check_connectivity(
+      exec_ctx, glb_policy->rr_policy, &error);
+  grpc_lb_policy_notify_on_state_change(
+      exec_ctx, glb_policy->rr_policy, &glb_policy->rr_connectivity->state,
+      &glb_policy->rr_connectivity->on_change);
+  grpc_connectivity_state_set(exec_ctx, &glb_policy->state_tracker,
+                              glb_policy->rr_connectivity->state,
+                              GRPC_ERROR_REF(error), "rr_handover");
+  grpc_lb_policy_exit_idle(exec_ctx, glb_policy->rr_policy);
+
+  /* flush pending ops */
+  pending_pick *pp;
+  while ((pp = glb_policy->pending_picks)) {
+    glb_policy->pending_picks = pp->next;
+    GRPC_LB_POLICY_REF(glb_policy->rr_policy, "rr_handover_pending_pick");
+    pp->wrapped_on_complete_arg.rr_policy = glb_policy->rr_policy;
+    if (grpc_lb_glb_trace) {
+      gpr_log(GPR_INFO, "Pending pick about to PICK from 0x%" PRIxPTR "",
+              (intptr_t)glb_policy->rr_policy);
+    }
+    grpc_lb_policy_pick(exec_ctx, glb_policy->rr_policy, &pp->pick_args,
+                        pp->target,
+                        (void **)&pp->wrapped_on_complete_arg.lb_token,
+                        &pp->wrapped_on_complete);
+    pp->wrapped_on_complete_arg.owning_pending_node = pp;
+  }
+
+  pending_ping *pping;
+  while ((pping = glb_policy->pending_pings)) {
+    glb_policy->pending_pings = pping->next;
+    GRPC_LB_POLICY_REF(glb_policy->rr_policy, "rr_handover_pending_ping");
+    pping->wrapped_notify_arg.rr_policy = glb_policy->rr_policy;
+    if (grpc_lb_glb_trace) {
+      gpr_log(GPR_INFO, "Pending ping about to PING from 0x%" PRIxPTR "",
+              (intptr_t)glb_policy->rr_policy);
+    }
+    grpc_lb_policy_ping_one(exec_ctx, glb_policy->rr_policy,
+                            &pping->wrapped_notify);
+    pping->wrapped_notify_arg.owning_pending_node = pping;
+  }
+}
+
+static void glb_rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
+                                        grpc_error *error) {
+  rr_connectivity_data *rr_conn_data = arg;
+  glb_lb_policy *glb_policy = rr_conn_data->glb_policy;
+
+  if (rr_conn_data->state == GRPC_CHANNEL_SHUTDOWN) {
+    if (glb_policy->serverlist != NULL) {
+      /* a RR policy is shutting down but there's a serverlist available ->
+       * perform a handover */
+      rr_handover(exec_ctx, glb_policy, error);
+    } else {
+      /* shutting down and no new serverlist available. Bail out. */
+      gpr_free(rr_conn_data);
+    }
+  } else {
+    if (error == GRPC_ERROR_NONE) {
+      /* RR not shutting down. Mimic the RR's policy state */
+      grpc_connectivity_state_set(exec_ctx, &glb_policy->state_tracker,
+                                  rr_conn_data->state, GRPC_ERROR_REF(error),
+                                  "glb_rr_connectivity_changed");
+      /* resubscribe */
+      grpc_lb_policy_notify_on_state_change(exec_ctx, glb_policy->rr_policy,
+                                            &rr_conn_data->state,
+                                            &rr_conn_data->on_change);
+    } else { /* error */
+      gpr_free(rr_conn_data);
+    }
+  }
+}
+
+static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx,
+                                  grpc_lb_policy_factory *factory,
+                                  grpc_lb_policy_args *args) {
+  /* Count the number of gRPC-LB addresses. There must be at least one.
+   * TODO(roth): For now, we ignore non-balancer addresses, but in the
+   * future, we may change the behavior such that we fall back to using
+   * the non-balancer addresses if we cannot reach any balancers. At that
+   * time, this should be changed to allow a list with no balancer addresses,
+   * since the resolver might fail to return a balancer address even when
+   * this is the right LB policy to use. */
+  size_t num_grpclb_addrs = 0;
+  for (size_t i = 0; i < args->addresses->num_addresses; ++i) {
+    if (args->addresses->addresses[i].is_balancer) ++num_grpclb_addrs;
+  }
+  if (num_grpclb_addrs == 0) return NULL;
+
+  glb_lb_policy *glb_policy = gpr_malloc(sizeof(*glb_policy));
+  memset(glb_policy, 0, sizeof(*glb_policy));
+
+  /* All input addresses in args->addresses come from a resolver that claims
+   * they are LB services. It's the resolver's responsibility to make sure
+   * this
+   * policy is only instantiated and used in that case.
+   *
+   * Create a client channel over them to communicate with a LB service */
+  glb_policy->server_name = gpr_strdup(args->server_name);
+  glb_policy->cc_factory = args->client_channel_factory;
+  GPR_ASSERT(glb_policy->cc_factory != NULL);
+
+  /* construct a target from the addresses in args, given in the form
+   * ipvX://ip1:port1,ip2:port2,...
+   * TODO(dgq): support mixed ip version */
+  char **addr_strs = gpr_malloc(sizeof(char *) * num_grpclb_addrs);
+  size_t addr_index = 0;
+  for (size_t i = 0; i < args->addresses->num_addresses; i++) {
+    if (args->addresses->addresses[i].user_data != NULL) {
+      gpr_log(GPR_ERROR,
+              "This LB policy doesn't support user data. It will be ignored");
+    }
+    if (args->addresses->addresses[i].is_balancer) {
+      if (addr_index == 0) {
+        addr_strs[addr_index++] = grpc_sockaddr_to_uri(
+            (const struct sockaddr *)&args->addresses->addresses[i]
+                .address.addr);
+      } else {
+        GPR_ASSERT(grpc_sockaddr_to_string(
+                       &addr_strs[addr_index++],
+                       (const struct sockaddr *)&args->addresses->addresses[i]
+                           .address.addr,
+                       true) > 0);
+      }
+    }
+  }
+  size_t uri_path_len;
+  char *target_uri_str = gpr_strjoin_sep((const char **)addr_strs,
+                                         num_grpclb_addrs, ",", &uri_path_len);
+
+  /* will pick using pick_first */
+  glb_policy->lb_channel = grpc_client_channel_factory_create_channel(
+      exec_ctx, glb_policy->cc_factory, target_uri_str,
+      GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, NULL);
+
+  gpr_free(target_uri_str);
+  for (size_t i = 0; i < num_grpclb_addrs; i++) {
+    gpr_free(addr_strs[i]);
+  }
+  gpr_free(addr_strs);
+
+  if (glb_policy->lb_channel == NULL) {
+    gpr_free(glb_policy);
+    return NULL;
+  }
+
+  rr_connectivity_data *rr_connectivity =
+      gpr_malloc(sizeof(rr_connectivity_data));
+  memset(rr_connectivity, 0, sizeof(rr_connectivity_data));
+  grpc_closure_init(&rr_connectivity->on_change, glb_rr_connectivity_changed,
+                    rr_connectivity);
+  rr_connectivity->glb_policy = glb_policy;
+  glb_policy->rr_connectivity = rr_connectivity;
+
+  grpc_lb_policy_init(&glb_policy->base, &glb_lb_policy_vtable);
+  gpr_mu_init(&glb_policy->mu);
+  grpc_connectivity_state_init(&glb_policy->state_tracker, GRPC_CHANNEL_IDLE,
+                               "grpclb");
+  return &glb_policy->base;
+}
+
+static void glb_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
+  glb_lb_policy *glb_policy = (glb_lb_policy *)pol;
+  GPR_ASSERT(glb_policy->pending_picks == NULL);
+  GPR_ASSERT(glb_policy->pending_pings == NULL);
+  gpr_free((void *)glb_policy->server_name);
+  grpc_channel_destroy(glb_policy->lb_channel);
+  glb_policy->lb_channel = NULL;
+  grpc_connectivity_state_destroy(exec_ctx, &glb_policy->state_tracker);
+  if (glb_policy->serverlist != NULL) {
+    grpc_grpclb_destroy_serverlist(glb_policy->serverlist);
+  }
+  gpr_mu_destroy(&glb_policy->mu);
+  grpc_lb_addresses_destroy(glb_policy->addresses, lb_token_destroy);
+  gpr_free(glb_policy);
+}
+
+static void lb_client_data_destroy(struct lb_client_data *lb_client);
+static void glb_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
+  glb_lb_policy *glb_policy = (glb_lb_policy *)pol;
+  gpr_mu_lock(&glb_policy->mu);
+
+  pending_pick *pp = glb_policy->pending_picks;
+  glb_policy->pending_picks = NULL;
+  pending_ping *pping = glb_policy->pending_pings;
+  glb_policy->pending_pings = NULL;
+  gpr_mu_unlock(&glb_policy->mu);
+
+  while (pp != NULL) {
+    pending_pick *next = pp->next;
+    *pp->target = NULL;
+    grpc_exec_ctx_sched(exec_ctx, &pp->wrapped_on_complete, GRPC_ERROR_NONE,
+                        NULL);
+    pp = next;
+  }
+
+  while (pping != NULL) {
+    pending_ping *next = pping->next;
+    grpc_exec_ctx_sched(exec_ctx, &pping->wrapped_notify, GRPC_ERROR_NONE,
+                        NULL);
+    pping = next;
+  }
+
+  if (glb_policy->rr_policy) {
+    /* unsubscribe */
+    grpc_lb_policy_notify_on_state_change(
+        exec_ctx, glb_policy->rr_policy, NULL,
+        &glb_policy->rr_connectivity->on_change);
+    GRPC_LB_POLICY_UNREF(exec_ctx, glb_policy->rr_policy, "glb_shutdown");
+  }
+
+  lb_client_data_destroy(glb_policy->lb_client);
+  glb_policy->lb_client = NULL;
+
+  grpc_connectivity_state_set(
+      exec_ctx, &glb_policy->state_tracker, GRPC_CHANNEL_SHUTDOWN,
+      GRPC_ERROR_CREATE("Channel Shutdown"), "glb_shutdown");
+}
+
+static void glb_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
+                            grpc_connected_subchannel **target,
+                            grpc_error *error) {
+  glb_lb_policy *glb_policy = (glb_lb_policy *)pol;
+  gpr_mu_lock(&glb_policy->mu);
+  pending_pick *pp = glb_policy->pending_picks;
+  glb_policy->pending_picks = NULL;
+  while (pp != NULL) {
+    pending_pick *next = pp->next;
+    if (pp->target == target) {
+      grpc_polling_entity_del_from_pollset_set(
+          exec_ctx, pp->pick_args.pollent, glb_policy->base.interested_parties);
+      *target = NULL;
+      grpc_exec_ctx_sched(
+          exec_ctx, &pp->wrapped_on_complete,
+          GRPC_ERROR_CREATE_REFERENCING("Pick Cancelled", &error, 1), NULL);
+    } else {
+      pp->next = glb_policy->pending_picks;
+      glb_policy->pending_picks = pp;
+    }
+    pp = next;
+  }
+  gpr_mu_unlock(&glb_policy->mu);
+  GRPC_ERROR_UNREF(error);
+}
+
+static grpc_call *lb_client_data_get_call(struct lb_client_data *lb_client);
+static void glb_cancel_picks(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
+                             uint32_t initial_metadata_flags_mask,
+                             uint32_t initial_metadata_flags_eq,
+                             grpc_error *error) {
+  glb_lb_policy *glb_policy = (glb_lb_policy *)pol;
+  gpr_mu_lock(&glb_policy->mu);
+  if (glb_policy->lb_client != NULL) {
+    /* cancel the call to the load balancer service, if any */
+    grpc_call_cancel(lb_client_data_get_call(glb_policy->lb_client), NULL);
+  }
+  pending_pick *pp = glb_policy->pending_picks;
+  glb_policy->pending_picks = NULL;
+  while (pp != NULL) {
+    pending_pick *next = pp->next;
+    if ((pp->pick_args.initial_metadata_flags & initial_metadata_flags_mask) ==
+        initial_metadata_flags_eq) {
+      grpc_polling_entity_del_from_pollset_set(
+          exec_ctx, pp->pick_args.pollent, glb_policy->base.interested_parties);
+      grpc_exec_ctx_sched(
+          exec_ctx, &pp->wrapped_on_complete,
+          GRPC_ERROR_CREATE_REFERENCING("Pick Cancelled", &error, 1), NULL);
+    } else {
+      pp->next = glb_policy->pending_picks;
+      glb_policy->pending_picks = pp;
+    }
+    pp = next;
+  }
+  gpr_mu_unlock(&glb_policy->mu);
+  GRPC_ERROR_UNREF(error);
+}
+
+static void query_for_backends(grpc_exec_ctx *exec_ctx,
+                               glb_lb_policy *glb_policy);
+static void start_picking(grpc_exec_ctx *exec_ctx, glb_lb_policy *glb_policy) {
+  glb_policy->started_picking = true;
+  query_for_backends(exec_ctx, glb_policy);
+}
+
+static void glb_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
+  glb_lb_policy *glb_policy = (glb_lb_policy *)pol;
+  gpr_mu_lock(&glb_policy->mu);
+  if (!glb_policy->started_picking) {
+    start_picking(exec_ctx, glb_policy);
+  }
+  gpr_mu_unlock(&glb_policy->mu);
+}
+
+static int glb_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
+                    const grpc_lb_policy_pick_args *pick_args,
+                    grpc_connected_subchannel **target, void **user_data,
+                    grpc_closure *on_complete) {
+  if (pick_args->lb_token_mdelem_storage == NULL) {
+    *target = NULL;
+    grpc_exec_ctx_sched(
+        exec_ctx, on_complete,
+        GRPC_ERROR_CREATE("No mdelem storage for the LB token. Load reporting "
+                          "won't work without it. Failing"),
+        NULL);
+    return 1;
+  }
+
+  glb_lb_policy *glb_policy = (glb_lb_policy *)pol;
+  gpr_mu_lock(&glb_policy->mu);
+  glb_policy->deadline = pick_args->deadline;
+  bool pick_done;
+
+  if (glb_policy->rr_policy != NULL) {
+    if (grpc_lb_glb_trace) {
+      gpr_log(GPR_INFO, "about to PICK from 0x%" PRIxPTR "",
+              (intptr_t)glb_policy->rr_policy);
+    }
+    GRPC_LB_POLICY_REF(glb_policy->rr_policy, "glb_pick");
+    memset(&glb_policy->wc_arg, 0, sizeof(wrapped_rr_closure_arg));
+    glb_policy->wc_arg.rr_policy = glb_policy->rr_policy;
+    glb_policy->wc_arg.target = target;
+    glb_policy->wc_arg.wrapped_closure = on_complete;
+    glb_policy->wc_arg.lb_token_mdelem_storage =
+        pick_args->lb_token_mdelem_storage;
+    glb_policy->wc_arg.initial_metadata = pick_args->initial_metadata;
+    glb_policy->wc_arg.owning_pending_node = NULL;
+    grpc_closure_init(&glb_policy->wrapped_on_complete, wrapped_rr_closure,
+                      &glb_policy->wc_arg);
+
+    pick_done =
+        grpc_lb_policy_pick(exec_ctx, glb_policy->rr_policy, pick_args, target,
+                            (void **)&glb_policy->wc_arg.lb_token,
+                            &glb_policy->wrapped_on_complete);
+    if (pick_done) {
+      /* synchronous grpc_lb_policy_pick call. Unref the RR policy. */
+      if (grpc_lb_glb_trace) {
+        gpr_log(GPR_INFO, "Unreffing RR (0x%" PRIxPTR ")",
+                (intptr_t)glb_policy->wc_arg.rr_policy);
+      }
+      GRPC_LB_POLICY_UNREF(exec_ctx, glb_policy->wc_arg.rr_policy, "glb_pick");
+
+      /* add the load reporting initial metadata */
+      initial_metadata_add_lb_token(
+          pick_args->initial_metadata, pick_args->lb_token_mdelem_storage,
+          GRPC_MDELEM_REF(glb_policy->wc_arg.lb_token));
+    }
+  } else {
+    /* else, the pending pick will be registered and taken care of by the
+     * pending pick list inside the RR policy (glb_policy->rr_policy) */
+    grpc_polling_entity_add_to_pollset_set(exec_ctx, pick_args->pollent,
+                                           glb_policy->base.interested_parties);
+    add_pending_pick(&glb_policy->pending_picks, pick_args, target,
+                     on_complete);
+
+    if (!glb_policy->started_picking) {
+      start_picking(exec_ctx, glb_policy);
+    }
+    pick_done = false;
+  }
+  gpr_mu_unlock(&glb_policy->mu);
+  return pick_done;
+}
+
+static grpc_connectivity_state glb_check_connectivity(
+    grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
+    grpc_error **connectivity_error) {
+  glb_lb_policy *glb_policy = (glb_lb_policy *)pol;
+  grpc_connectivity_state st;
+  gpr_mu_lock(&glb_policy->mu);
+  st = grpc_connectivity_state_check(&glb_policy->state_tracker,
+                                     connectivity_error);
+  gpr_mu_unlock(&glb_policy->mu);
+  return st;
+}
+
+static void glb_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
+                         grpc_closure *closure) {
+  glb_lb_policy *glb_policy = (glb_lb_policy *)pol;
+  gpr_mu_lock(&glb_policy->mu);
+  if (glb_policy->rr_policy) {
+    grpc_lb_policy_ping_one(exec_ctx, glb_policy->rr_policy, closure);
+  } else {
+    add_pending_ping(&glb_policy->pending_pings, closure);
+    if (!glb_policy->started_picking) {
+      start_picking(exec_ctx, glb_policy);
+    }
+  }
+  gpr_mu_unlock(&glb_policy->mu);
+}
+
+static void glb_notify_on_state_change(grpc_exec_ctx *exec_ctx,
+                                       grpc_lb_policy *pol,
+                                       grpc_connectivity_state *current,
+                                       grpc_closure *notify) {
+  glb_lb_policy *glb_policy = (glb_lb_policy *)pol;
+  gpr_mu_lock(&glb_policy->mu);
+  grpc_connectivity_state_notify_on_state_change(
+      exec_ctx, &glb_policy->state_tracker, current, notify);
+
+  gpr_mu_unlock(&glb_policy->mu);
+}
+
+/*
+ * lb_client_data
+ *
+ * Used internally for the client call to the LB */
+typedef struct lb_client_data {
+  gpr_mu mu;
+
+  /* called once initial metadata's been sent */
+  grpc_closure md_sent;
+
+  /* called once the LoadBalanceRequest has been sent to the LB server. See
+   * src/proto/grpc/.../load_balancer.proto */
+  grpc_closure req_sent;
+
+  /* A response from the LB server has been received (or error). Process it */
+  grpc_closure res_rcvd;
+
+  /* After the client has sent a close to the LB server */
+  grpc_closure close_sent;
+
+  /* ... and the status from the LB server has been received */
+  grpc_closure srv_status_rcvd;
+
+  grpc_call *lb_call;    /* streaming call to the LB server, */
+  gpr_timespec deadline; /* for the streaming call to the LB server */
+
+  grpc_metadata_array initial_metadata_recv;  /* initial MD from LB server */
+  grpc_metadata_array trailing_metadata_recv; /* trailing MD from LB server */
+
+  /* what's being sent to the LB server. Note that its value may vary if the LB
+   * server indicates a redirect. */
+  grpc_byte_buffer *request_payload;
+
+  /* response from the LB server, if any. Processed in res_recv_cb() */
+  grpc_byte_buffer *response_payload;
+
+  /* the call's status and status detailset in srv_status_rcvd_cb() */
+  grpc_status_code status;
+  char *status_details;
+  size_t status_details_capacity;
+
+  /* pointer back to the enclosing policy */
+  glb_lb_policy *glb_policy;
+} lb_client_data;
+
+static void md_sent_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error);
+static void req_sent_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error);
+static void res_recv_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error);
+static void close_sent_cb(grpc_exec_ctx *exec_ctx, void *arg,
+                          grpc_error *error);
+static void srv_status_rcvd_cb(grpc_exec_ctx *exec_ctx, void *arg,
+                               grpc_error *error);
+
+static lb_client_data *lb_client_data_create(glb_lb_policy *glb_policy) {
+  GPR_ASSERT(glb_policy->server_name != NULL);
+  GPR_ASSERT(glb_policy->server_name[0] != '\0');
+
+  lb_client_data *lb_client = gpr_malloc(sizeof(lb_client_data));
+  memset(lb_client, 0, sizeof(lb_client_data));
+
+  gpr_mu_init(&lb_client->mu);
+  grpc_closure_init(&lb_client->md_sent, md_sent_cb, lb_client);
+
+  grpc_closure_init(&lb_client->req_sent, req_sent_cb, lb_client);
+  grpc_closure_init(&lb_client->res_rcvd, res_recv_cb, lb_client);
+  grpc_closure_init(&lb_client->close_sent, close_sent_cb, lb_client);
+  grpc_closure_init(&lb_client->srv_status_rcvd, srv_status_rcvd_cb, lb_client);
+
+  lb_client->deadline = glb_policy->deadline;
+
+  /* Note the following LB call progresses every time there's activity in \a
+   * glb_policy->base.interested_parties, which is comprised of the polling
+   * entities passed to glb_pick(). */
+  lb_client->lb_call = grpc_channel_create_pollset_set_call(
+      glb_policy->lb_channel, NULL, GRPC_PROPAGATE_DEFAULTS,
+      glb_policy->base.interested_parties,
+      "/grpc.lb.v1.LoadBalancer/BalanceLoad", glb_policy->server_name,
+      lb_client->deadline, NULL);
+
+  grpc_metadata_array_init(&lb_client->initial_metadata_recv);
+  grpc_metadata_array_init(&lb_client->trailing_metadata_recv);
+
+  grpc_grpclb_request *request =
+      grpc_grpclb_request_create(glb_policy->server_name);
+  gpr_slice request_payload_slice = grpc_grpclb_request_encode(request);
+  lb_client->request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  gpr_slice_unref(request_payload_slice);
+  grpc_grpclb_request_destroy(request);
+
+  lb_client->status_details = NULL;
+  lb_client->status_details_capacity = 0;
+  lb_client->glb_policy = glb_policy;
+  return lb_client;
+}
+
+static void lb_client_data_destroy(lb_client_data *lb_client) {
+  grpc_call_destroy(lb_client->lb_call);
+  grpc_metadata_array_destroy(&lb_client->initial_metadata_recv);
+  grpc_metadata_array_destroy(&lb_client->trailing_metadata_recv);
+
+  grpc_byte_buffer_destroy(lb_client->request_payload);
+
+  gpr_free(lb_client->status_details);
+  gpr_mu_destroy(&lb_client->mu);
+  gpr_free(lb_client);
+}
+static grpc_call *lb_client_data_get_call(lb_client_data *lb_client) {
+  return lb_client->lb_call;
+}
+
+/*
+ * Auxiliary functions and LB client callbacks.
+ */
+static void query_for_backends(grpc_exec_ctx *exec_ctx,
+                               glb_lb_policy *glb_policy) {
+  GPR_ASSERT(glb_policy->lb_channel != NULL);
+
+  glb_policy->lb_client = lb_client_data_create(glb_policy);
+  grpc_call_error call_error;
+  grpc_op ops[1];
+  memset(ops, 0, sizeof(ops));
+  grpc_op *op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  call_error = grpc_call_start_batch_and_execute(
+      exec_ctx, glb_policy->lb_client->lb_call, ops, (size_t)(op - ops),
+      &glb_policy->lb_client->md_sent);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+
+  op = ops;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata =
+      &glb_policy->lb_client->trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &glb_policy->lb_client->status;
+  op->data.recv_status_on_client.status_details =
+      &glb_policy->lb_client->status_details;
+  op->data.recv_status_on_client.status_details_capacity =
+      &glb_policy->lb_client->status_details_capacity;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  call_error = grpc_call_start_batch_and_execute(
+      exec_ctx, glb_policy->lb_client->lb_call, ops, (size_t)(op - ops),
+      &glb_policy->lb_client->srv_status_rcvd);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+}
+
+static void md_sent_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  lb_client_data *lb_client = arg;
+  GPR_ASSERT(lb_client->lb_call);
+  grpc_op ops[1];
+  memset(ops, 0, sizeof(ops));
+  grpc_op *op = ops;
+
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message = lb_client->request_payload;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  grpc_call_error call_error = grpc_call_start_batch_and_execute(
+      exec_ctx, lb_client->lb_call, ops, (size_t)(op - ops),
+      &lb_client->req_sent);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+}
+
+static void req_sent_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  lb_client_data *lb_client = arg;
+  GPR_ASSERT(lb_client->lb_call);
+
+  grpc_op ops[2];
+  memset(ops, 0, sizeof(ops));
+  grpc_op *op = ops;
+
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata = &lb_client->initial_metadata_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message = &lb_client->response_payload;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  grpc_call_error call_error = grpc_call_start_batch_and_execute(
+      exec_ctx, lb_client->lb_call, ops, (size_t)(op - ops),
+      &lb_client->res_rcvd);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+}
+
+static void res_recv_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  lb_client_data *lb_client = arg;
+  grpc_op ops[2];
+  memset(ops, 0, sizeof(ops));
+  grpc_op *op = ops;
+  if (lb_client->response_payload != NULL) {
+    /* Received data from the LB server. Look inside
+     * lb_client->response_payload, for a serverlist. */
+    grpc_byte_buffer_reader bbr;
+    grpc_byte_buffer_reader_init(&bbr, lb_client->response_payload);
+    gpr_slice response_slice = grpc_byte_buffer_reader_readall(&bbr);
+    grpc_byte_buffer_destroy(lb_client->response_payload);
+    grpc_grpclb_serverlist *serverlist =
+        grpc_grpclb_response_parse_serverlist(response_slice);
+    if (serverlist != NULL) {
+      gpr_slice_unref(response_slice);
+      if (grpc_lb_glb_trace) {
+        gpr_log(GPR_INFO, "Serverlist with %zu servers received",
+                serverlist->num_servers);
+      }
+
+      /* update serverlist */
+      if (serverlist->num_servers > 0) {
+        if (grpc_grpclb_serverlist_equals(lb_client->glb_policy->serverlist,
+                                          serverlist)) {
+          if (grpc_lb_glb_trace) {
+            gpr_log(GPR_INFO,
+                    "Incoming server list identical to current, ignoring.");
+          }
+        } else { /* new serverlist */
+          if (lb_client->glb_policy->serverlist != NULL) {
+            /* dispose of the old serverlist */
+            grpc_grpclb_destroy_serverlist(lb_client->glb_policy->serverlist);
+          }
+          /* and update the copy in the glb_lb_policy instance */
+          lb_client->glb_policy->serverlist = serverlist;
+        }
+        if (lb_client->glb_policy->rr_policy == NULL) {
+          /* initial "handover", in this case from a null RR policy, meaning
+           * it'll just create the first RR policy instance */
+          rr_handover(exec_ctx, lb_client->glb_policy, error);
+        } else {
+          /* unref the RR policy, eventually leading to its substitution with a
+           * new one constructed from the received serverlist (see
+           * glb_rr_connectivity_changed) */
+          GRPC_LB_POLICY_UNREF(exec_ctx, lb_client->glb_policy->rr_policy,
+                               "serverlist_received");
+        }
+      } else {
+        if (grpc_lb_glb_trace) {
+          gpr_log(GPR_INFO,
+                  "Received empty server list. Picks will stay pending until a "
+                  "response with > 0 servers is received");
+        }
+      }
+
+      /* keep listening for serverlist updates */
+      op->op = GRPC_OP_RECV_MESSAGE;
+      op->data.recv_message = &lb_client->response_payload;
+      op->flags = 0;
+      op->reserved = NULL;
+      op++;
+      const grpc_call_error call_error = grpc_call_start_batch_and_execute(
+          exec_ctx, lb_client->lb_call, ops, (size_t)(op - ops),
+          &lb_client->res_rcvd); /* loop */
+      GPR_ASSERT(GRPC_CALL_OK == call_error);
+      return;
+    }
+
+    GPR_ASSERT(serverlist == NULL);
+    gpr_log(GPR_ERROR, "Invalid LB response received: '%s'",
+            gpr_dump_slice(response_slice, GPR_DUMP_ASCII));
+    gpr_slice_unref(response_slice);
+
+    /* Disconnect from server returning invalid response. */
+    op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+    op->flags = 0;
+    op->reserved = NULL;
+    op++;
+    grpc_call_error call_error = grpc_call_start_batch_and_execute(
+        exec_ctx, lb_client->lb_call, ops, (size_t)(op - ops),
+        &lb_client->close_sent);
+    GPR_ASSERT(GRPC_CALL_OK == call_error);
+  }
+  /* empty payload: call cancelled by server. Cleanups happening in
+   * srv_status_rcvd_cb */
+}
+
+static void close_sent_cb(grpc_exec_ctx *exec_ctx, void *arg,
+                          grpc_error *error) {
+  if (grpc_lb_glb_trace) {
+    gpr_log(GPR_INFO,
+            "Close from LB client sent. Waiting from server status now");
+  }
+}
+
+static void srv_status_rcvd_cb(grpc_exec_ctx *exec_ctx, void *arg,
+                               grpc_error *error) {
+  lb_client_data *lb_client = arg;
+  if (grpc_lb_glb_trace) {
+    gpr_log(GPR_INFO,
+            "status from lb server received. Status = %d, Details = '%s', "
+            "Capaticy "
+            "= %zu",
+            lb_client->status, lb_client->status_details,
+            lb_client->status_details_capacity);
+  }
+  /* TODO(dgq): deal with stream termination properly (fire up another one?
+   * fail the original call?) */
+}
+
+/* Code wiring the policy with the rest of the core */
+static const grpc_lb_policy_vtable glb_lb_policy_vtable = {
+    glb_destroy,     glb_shutdown,           glb_pick,
+    glb_cancel_pick, glb_cancel_picks,       glb_ping_one,
+    glb_exit_idle,   glb_check_connectivity, glb_notify_on_state_change};
+
+static void glb_factory_ref(grpc_lb_policy_factory *factory) {}
+
+static void glb_factory_unref(grpc_lb_policy_factory *factory) {}
+
+static const grpc_lb_policy_factory_vtable glb_factory_vtable = {
+    glb_factory_ref, glb_factory_unref, glb_create, "grpclb"};
+
+static grpc_lb_policy_factory glb_lb_policy_factory = {&glb_factory_vtable};
+
+grpc_lb_policy_factory *grpc_glb_lb_factory_create() {
+  return &glb_lb_policy_factory;
+}
+
+/* Plugin registration */
+void grpc_lb_policy_grpclb_init() {
+  grpc_register_lb_policy(grpc_glb_lb_factory_create());
+  grpc_register_tracer("glb", &grpc_lb_glb_trace);
+}
+
+void grpc_lb_policy_grpclb_shutdown() {}
diff --git a/src/core/ext/lb_policy/grpclb/grpclb.h b/src/core/ext/lb_policy/grpclb/grpclb.h
new file mode 100644
index 0000000..83552b4
--- /dev/null
+++ b/src/core/ext/lb_policy/grpclb/grpclb.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2016, 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_CORE_EXT_LB_POLICY_GRPCLB_GRPCLB_H
+#define GRPC_CORE_EXT_LB_POLICY_GRPCLB_GRPCLB_H
+
+#include "src/core/ext/client_config/lb_policy_factory.h"
+
+/** Returns a load balancing factory for the glb policy, which tries to connect
+ * to a load balancing server to decide the next successfully connected
+ * subchannel to pick. */
+grpc_lb_policy_factory *grpc_glb_lb_factory_create();
+
+#endif /* GRPC_CORE_EXT_LB_POLICY_GRPCLB_GRPCLB_H */
diff --git a/src/core/ext/lb_policy/grpclb/load_balancer_api.c b/src/core/ext/lb_policy/grpclb/load_balancer_api.c
index 59b8999..a888100 100644
--- a/src/core/ext/lb_policy/grpclb/load_balancer_api.c
+++ b/src/core/ext/lb_policy/grpclb/load_balancer_api.c
@@ -38,9 +38,15 @@
 #include <grpc/support/alloc.h>
 
 typedef struct decode_serverlist_arg {
-  int first_pass;
-  int i;
+  /* The first pass counts the number of servers in the server list. The second
+   * one allocates and decodes. */
+  bool first_pass;
+  /* The decoding callback is invoked once per server in serverlist. Remember
+   * which index of the serverlist are we currently decoding */
+  size_t decoding_idx;
+  /* Populated after the first pass. Number of server in the input serverlist */
   size_t num_servers;
+  /* The decoded serverlist */
   grpc_grpclb_server **servers;
 } decode_serverlist_arg;
 
@@ -48,23 +54,26 @@
 static bool decode_serverlist(pb_istream_t *stream, const pb_field_t *field,
                               void **arg) {
   decode_serverlist_arg *dec_arg = *arg;
-  if (dec_arg->first_pass != 0) { /* first pass */
+  if (dec_arg->first_pass) { /* count how many server do we have */
     grpc_grpclb_server server;
     if (!pb_decode(stream, grpc_lb_v1_Server_fields, &server)) {
+      gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream));
       return false;
     }
     dec_arg->num_servers++;
-  } else { /* second pass */
+  } else { /* second pass. Actually decode. */
     grpc_grpclb_server *server = gpr_malloc(sizeof(grpc_grpclb_server));
+    memset(server, 0, sizeof(grpc_grpclb_server));
     GPR_ASSERT(dec_arg->num_servers > 0);
-    if (dec_arg->i == 0) { /* first iteration of second pass */
+    if (dec_arg->decoding_idx == 0) { /* first iteration of second pass */
       dec_arg->servers =
           gpr_malloc(sizeof(grpc_grpclb_server *) * dec_arg->num_servers);
     }
     if (!pb_decode(stream, grpc_lb_v1_Server_fields, server)) {
+      gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream));
       return false;
     }
-    dec_arg->servers[dec_arg->i++] = server;
+    dec_arg->servers[dec_arg->decoding_idx++] = server;
   }
 
   return true;
@@ -102,57 +111,62 @@
   gpr_free(request);
 }
 
-grpc_grpclb_response *grpc_grpclb_response_parse(gpr_slice encoded_response) {
-  bool status;
+typedef grpc_lb_v1_LoadBalanceResponse grpc_grpclb_response;
+grpc_grpclb_initial_response *grpc_grpclb_initial_response_parse(
+    gpr_slice encoded_grpc_grpclb_response) {
   pb_istream_t stream =
-      pb_istream_from_buffer(GPR_SLICE_START_PTR(encoded_response),
-                             GPR_SLICE_LENGTH(encoded_response));
-  grpc_grpclb_response *res = gpr_malloc(sizeof(grpc_grpclb_response));
-  memset(res, 0, sizeof(*res));
-  status = pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, res);
-  if (!status) {
-    grpc_grpclb_response_destroy(res);
+      pb_istream_from_buffer(GPR_SLICE_START_PTR(encoded_grpc_grpclb_response),
+                             GPR_SLICE_LENGTH(encoded_grpc_grpclb_response));
+  grpc_grpclb_response res;
+  memset(&res, 0, sizeof(grpc_grpclb_response));
+  if (!pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
     return NULL;
   }
-  return res;
+  grpc_grpclb_initial_response *initial_res =
+      gpr_malloc(sizeof(grpc_grpclb_initial_response));
+  memcpy(initial_res, &res.initial_response,
+         sizeof(grpc_grpclb_initial_response));
+
+  return initial_res;
 }
 
 grpc_grpclb_serverlist *grpc_grpclb_response_parse_serverlist(
-    gpr_slice encoded_response) {
+    gpr_slice encoded_grpc_grpclb_response) {
   bool status;
   decode_serverlist_arg arg;
   pb_istream_t stream =
-      pb_istream_from_buffer(GPR_SLICE_START_PTR(encoded_response),
-                             GPR_SLICE_LENGTH(encoded_response));
+      pb_istream_from_buffer(GPR_SLICE_START_PTR(encoded_grpc_grpclb_response),
+                             GPR_SLICE_LENGTH(encoded_grpc_grpclb_response));
   pb_istream_t stream_at_start = stream;
-  grpc_grpclb_response *res = gpr_malloc(sizeof(grpc_grpclb_response));
-  memset(res, 0, sizeof(*res));
+  grpc_grpclb_response res;
+  memset(&res, 0, sizeof(grpc_grpclb_response));
   memset(&arg, 0, sizeof(decode_serverlist_arg));
 
-  res->server_list.servers.funcs.decode = decode_serverlist;
-  res->server_list.servers.arg = &arg;
-  arg.first_pass = 1;
-  status = pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, res);
+  res.server_list.servers.funcs.decode = decode_serverlist;
+  res.server_list.servers.arg = &arg;
+  arg.first_pass = true;
+  status = pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res);
   if (!status) {
-    grpc_grpclb_response_destroy(res);
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
     return NULL;
   }
 
-  arg.first_pass = 0;
+  arg.first_pass = false;
   status =
-      pb_decode(&stream_at_start, grpc_lb_v1_LoadBalanceResponse_fields, res);
+      pb_decode(&stream_at_start, grpc_lb_v1_LoadBalanceResponse_fields, &res);
   if (!status) {
-    grpc_grpclb_response_destroy(res);
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
     return NULL;
   }
 
   grpc_grpclb_serverlist *sl = gpr_malloc(sizeof(grpc_grpclb_serverlist));
+  memset(sl, 0, sizeof(*sl));
   sl->num_servers = arg.num_servers;
   sl->servers = arg.servers;
-  if (res->server_list.has_expiration_interval) {
-    sl->expiration_interval = res->server_list.expiration_interval;
+  if (res.server_list.has_expiration_interval) {
+    sl->expiration_interval = res.server_list.expiration_interval;
   }
-  grpc_grpclb_response_destroy(res);
   return sl;
 }
 
@@ -167,6 +181,72 @@
   gpr_free(serverlist);
 }
 
-void grpc_grpclb_response_destroy(grpc_grpclb_response *response) {
+grpc_grpclb_serverlist *grpc_grpclb_serverlist_copy(
+    const grpc_grpclb_serverlist *sl) {
+  grpc_grpclb_serverlist *copy = gpr_malloc(sizeof(grpc_grpclb_serverlist));
+  memset(copy, 0, sizeof(grpc_grpclb_serverlist));
+  copy->num_servers = sl->num_servers;
+  memcpy(&copy->expiration_interval, &sl->expiration_interval,
+         sizeof(grpc_grpclb_duration));
+  copy->servers = gpr_malloc(sizeof(grpc_grpclb_server *) * sl->num_servers);
+  for (size_t i = 0; i < sl->num_servers; i++) {
+    copy->servers[i] = gpr_malloc(sizeof(grpc_grpclb_server));
+    memcpy(copy->servers[i], sl->servers[i], sizeof(grpc_grpclb_server));
+  }
+  return copy;
+}
+
+bool grpc_grpclb_serverlist_equals(const grpc_grpclb_serverlist *lhs,
+                                   const grpc_grpclb_serverlist *rhs) {
+  if ((lhs == NULL) || (rhs == NULL)) {
+    return false;
+  }
+  if (lhs->num_servers != rhs->num_servers) {
+    return false;
+  }
+  if (grpc_grpclb_duration_compare(&lhs->expiration_interval,
+                                   &rhs->expiration_interval) != 0) {
+    return false;
+  }
+  for (size_t i = 0; i < lhs->num_servers; i++) {
+    if (!grpc_grpclb_server_equals(lhs->servers[i], rhs->servers[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool grpc_grpclb_server_equals(const grpc_grpclb_server *lhs,
+                               const grpc_grpclb_server *rhs) {
+  return memcmp(lhs, rhs, sizeof(grpc_grpclb_server)) == 0;
+}
+
+int grpc_grpclb_duration_compare(const grpc_grpclb_duration *lhs,
+                                 const grpc_grpclb_duration *rhs) {
+  GPR_ASSERT(lhs && rhs);
+  if (lhs->has_seconds && rhs->has_seconds) {
+    if (lhs->seconds < rhs->seconds) return -1;
+    if (lhs->seconds > rhs->seconds) return 1;
+  } else if (lhs->has_seconds) {
+    return 1;
+  } else if (rhs->has_seconds) {
+    return -1;
+  }
+
+  GPR_ASSERT(lhs->seconds == rhs->seconds);
+  if (lhs->has_nanos && rhs->has_nanos) {
+    if (lhs->nanos < rhs->nanos) return -1;
+    if (lhs->nanos > rhs->nanos) return 1;
+  } else if (lhs->has_nanos) {
+    return 1;
+  } else if (rhs->has_nanos) {
+    return -1;
+  }
+
+  return 0;
+}
+
+void grpc_grpclb_initial_response_destroy(
+    grpc_grpclb_initial_response *response) {
   gpr_free(response);
 }
diff --git a/src/core/ext/lb_policy/grpclb/load_balancer_api.h b/src/core/ext/lb_policy/grpclb/load_balancer_api.h
index 71b5616..c1e73d0 100644
--- a/src/core/ext/lb_policy/grpclb/load_balancer_api.h
+++ b/src/core/ext/lb_policy/grpclb/load_balancer_api.h
@@ -45,8 +45,9 @@
 
 #define GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH 128
 
+typedef grpc_lb_v1_Server_ip_address_t grpc_grpclb_ip_address;
 typedef grpc_lb_v1_LoadBalanceRequest grpc_grpclb_request;
-typedef grpc_lb_v1_LoadBalanceResponse grpc_grpclb_response;
+typedef grpc_lb_v1_InitialLoadBalanceResponse grpc_grpclb_initial_response;
 typedef grpc_lb_v1_Server grpc_grpclb_server;
 typedef grpc_lb_v1_Duration grpc_grpclb_duration;
 typedef struct grpc_grpclb_serverlist {
@@ -64,19 +65,37 @@
 /** Destroy \a request */
 void grpc_grpclb_request_destroy(grpc_grpclb_request *request);
 
-/** Parse (ie, decode) the bytes in \a encoded_response as a \a
- * grpc_grpclb_response */
-grpc_grpclb_response *grpc_grpclb_response_parse(gpr_slice encoded_response);
+/** Parse (ie, decode) the bytes in \a encoded_grpc_grpclb_response as a \a
+ * grpc_grpclb_initial_response */
+grpc_grpclb_initial_response *grpc_grpclb_initial_response_parse(
+    gpr_slice encoded_grpc_grpclb_response);
+
+/** Parse the list of servers from an encoded \a grpc_grpclb_response */
+grpc_grpclb_serverlist *grpc_grpclb_response_parse_serverlist(
+    gpr_slice encoded_grpc_grpclb_response);
+
+/** Return a copy of \a sl. The caller is responsible for calling \a
+ * grpc_grpclb_destroy_serverlist on the returned copy. */
+grpc_grpclb_serverlist *grpc_grpclb_serverlist_copy(
+    const grpc_grpclb_serverlist *sl);
+
+bool grpc_grpclb_serverlist_equals(const grpc_grpclb_serverlist *lhs,
+                                   const grpc_grpclb_serverlist *rhs);
+
+bool grpc_grpclb_server_equals(const grpc_grpclb_server *lhs,
+                               const grpc_grpclb_server *rhs);
 
 /** Destroy \a serverlist */
 void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist *serverlist);
 
-/** Parse the list of servers from an encoded \a grpc_grpclb_response */
-grpc_grpclb_serverlist *grpc_grpclb_response_parse_serverlist(
-    gpr_slice encoded_response);
+/** Compare \a lhs against \a rhs and return 0 if \a lhs and \a rhs are equal,
+ * < 0 if \a lhs represents a duration shorter than \a rhs and > 0 otherwise */
+int grpc_grpclb_duration_compare(const grpc_grpclb_duration *lhs,
+                                 const grpc_grpclb_duration *rhs);
 
-/** Destroy \a response */
-void grpc_grpclb_response_destroy(grpc_grpclb_response *response);
+/** Destroy \a initial_response */
+void grpc_grpclb_initial_response_destroy(
+    grpc_grpclb_initial_response *response);
 
 #ifdef __cplusplus
 }
diff --git a/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c b/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
index 52e11c4..afecb71 100644
--- a/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
+++ b/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
@@ -31,10 +31,11 @@
  *
  */
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.3.5-dev */
+/* Generated by nanopb-0.3.7-dev */
 
 #include "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h"
 
+/* @@protoc_insertion_point(includes) */
 #if PB_PROTO_HEADER_VERSION != 30
 #error Regenerate this file with the current version of nanopb generator.
 #endif
@@ -72,8 +73,8 @@
 };
 
 const pb_field_t grpc_lb_v1_InitialLoadBalanceResponse_fields[3] = {
-    PB_FIELD(  2, STRING  , OPTIONAL, STATIC  , FIRST, grpc_lb_v1_InitialLoadBalanceResponse, load_balancer_delegate, load_balancer_delegate, 0),
-    PB_FIELD(  3, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval, load_balancer_delegate, &grpc_lb_v1_Duration_fields),
+    PB_FIELD(  1, STRING  , OPTIONAL, STATIC  , FIRST, grpc_lb_v1_InitialLoadBalanceResponse, load_balancer_delegate, load_balancer_delegate, 0),
+    PB_FIELD(  2, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval, load_balancer_delegate, &grpc_lb_v1_Duration_fields),
     PB_LAST_FIELD
 };
 
@@ -84,7 +85,7 @@
 };
 
 const pb_field_t grpc_lb_v1_Server_fields[5] = {
-    PB_FIELD(  1, STRING  , OPTIONAL, STATIC  , FIRST, grpc_lb_v1_Server, ip_address, ip_address, 0),
+    PB_FIELD(  1, BYTES   , OPTIONAL, STATIC  , FIRST, grpc_lb_v1_Server, ip_address, ip_address, 0),
     PB_FIELD(  2, INT32   , OPTIONAL, STATIC  , OTHER, grpc_lb_v1_Server, port, ip_address, 0),
     PB_FIELD(  3, STRING  , OPTIONAL, STATIC  , OTHER, grpc_lb_v1_Server, load_balance_token, port, 0),
     PB_FIELD(  4, BOOL    , OPTIONAL, STATIC  , OTHER, grpc_lb_v1_Server, drop_request, load_balance_token, 0),
@@ -116,3 +117,4 @@
 #endif
 
 
+/* @@protoc_insertion_point(eof) */
diff --git a/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h b/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h
index 46fe588..53fed22 100644
--- a/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h
+++ b/src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h
@@ -31,11 +31,12 @@
  *
  */
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.3.5-dev */
+/* Generated by nanopb-0.3.7-dev */
 
-#ifndef GRPC_CORE_EXT_LB_POLICY_GRPCLB_PROTO_GRPC_LB_V1_LOAD_BALANCER_PB_H
-#define GRPC_CORE_EXT_LB_POLICY_GRPCLB_PROTO_GRPC_LB_V1_LOAD_BALANCER_PB_H
+#ifndef PB_GRPC_LB_V1_LOAD_BALANCER_PB_H_INCLUDED
+#define PB_GRPC_LB_V1_LOAD_BALANCER_PB_H_INCLUDED
 #include "third_party/nanopb/pb.h"
+/* @@protoc_insertion_point(includes) */
 #if PB_PROTO_HEADER_VERSION != 30
 #error Regenerate this file with the current version of nanopb generator.
 #endif
@@ -52,6 +53,7 @@
     int64_t client_rpc_errors;
     bool has_dropped_requests;
     int64_t dropped_requests;
+/* @@protoc_insertion_point(struct:grpc_lb_v1_ClientStats) */
 } grpc_lb_v1_ClientStats;
 
 typedef struct _grpc_lb_v1_Duration {
@@ -59,22 +61,26 @@
     int64_t seconds;
     bool has_nanos;
     int32_t nanos;
+/* @@protoc_insertion_point(struct:grpc_lb_v1_Duration) */
 } grpc_lb_v1_Duration;
 
 typedef struct _grpc_lb_v1_InitialLoadBalanceRequest {
     bool has_name;
     char name[128];
+/* @@protoc_insertion_point(struct:grpc_lb_v1_InitialLoadBalanceRequest) */
 } grpc_lb_v1_InitialLoadBalanceRequest;
 
+typedef PB_BYTES_ARRAY_T(16) grpc_lb_v1_Server_ip_address_t;
 typedef struct _grpc_lb_v1_Server {
     bool has_ip_address;
-    char ip_address[46];
+    grpc_lb_v1_Server_ip_address_t ip_address;
     bool has_port;
     int32_t port;
     bool has_load_balance_token;
-    char load_balance_token[64];
+    char load_balance_token[65];
     bool has_drop_request;
     bool drop_request;
+/* @@protoc_insertion_point(struct:grpc_lb_v1_Server) */
 } grpc_lb_v1_Server;
 
 typedef struct _grpc_lb_v1_InitialLoadBalanceResponse {
@@ -82,6 +88,7 @@
     char load_balancer_delegate[64];
     bool has_client_stats_report_interval;
     grpc_lb_v1_Duration client_stats_report_interval;
+/* @@protoc_insertion_point(struct:grpc_lb_v1_InitialLoadBalanceResponse) */
 } grpc_lb_v1_InitialLoadBalanceResponse;
 
 typedef struct _grpc_lb_v1_LoadBalanceRequest {
@@ -89,12 +96,14 @@
     grpc_lb_v1_InitialLoadBalanceRequest initial_request;
     bool has_client_stats;
     grpc_lb_v1_ClientStats client_stats;
+/* @@protoc_insertion_point(struct:grpc_lb_v1_LoadBalanceRequest) */
 } grpc_lb_v1_LoadBalanceRequest;
 
 typedef struct _grpc_lb_v1_ServerList {
     pb_callback_t servers;
     bool has_expiration_interval;
     grpc_lb_v1_Duration expiration_interval;
+/* @@protoc_insertion_point(struct:grpc_lb_v1_ServerList) */
 } grpc_lb_v1_ServerList;
 
 typedef struct _grpc_lb_v1_LoadBalanceResponse {
@@ -102,6 +111,7 @@
     grpc_lb_v1_InitialLoadBalanceResponse initial_response;
     bool has_server_list;
     grpc_lb_v1_ServerList server_list;
+/* @@protoc_insertion_point(struct:grpc_lb_v1_LoadBalanceResponse) */
 } grpc_lb_v1_LoadBalanceResponse;
 
 /* Default values for struct fields */
@@ -114,7 +124,7 @@
 #define grpc_lb_v1_LoadBalanceResponse_init_default {false, grpc_lb_v1_InitialLoadBalanceResponse_init_default, false, grpc_lb_v1_ServerList_init_default}
 #define grpc_lb_v1_InitialLoadBalanceResponse_init_default {false, "", false, grpc_lb_v1_Duration_init_default}
 #define grpc_lb_v1_ServerList_init_default       {{{NULL}, NULL}, false, grpc_lb_v1_Duration_init_default}
-#define grpc_lb_v1_Server_init_default           {false, "", false, 0, false, "", false, 0}
+#define grpc_lb_v1_Server_init_default           {false, {0, {0}}, false, 0, false, "", false, 0}
 #define grpc_lb_v1_Duration_init_zero            {false, 0, false, 0}
 #define grpc_lb_v1_LoadBalanceRequest_init_zero  {false, grpc_lb_v1_InitialLoadBalanceRequest_init_zero, false, grpc_lb_v1_ClientStats_init_zero}
 #define grpc_lb_v1_InitialLoadBalanceRequest_init_zero {false, ""}
@@ -122,7 +132,7 @@
 #define grpc_lb_v1_LoadBalanceResponse_init_zero {false, grpc_lb_v1_InitialLoadBalanceResponse_init_zero, false, grpc_lb_v1_ServerList_init_zero}
 #define grpc_lb_v1_InitialLoadBalanceResponse_init_zero {false, "", false, grpc_lb_v1_Duration_init_zero}
 #define grpc_lb_v1_ServerList_init_zero          {{{NULL}, NULL}, false, grpc_lb_v1_Duration_init_zero}
-#define grpc_lb_v1_Server_init_zero              {false, "", false, 0, false, "", false, 0}
+#define grpc_lb_v1_Server_init_zero              {false, {0, {0}}, false, 0, false, "", false, 0}
 
 /* Field tags (for use in manual encoding/decoding) */
 #define grpc_lb_v1_ClientStats_total_requests_tag 1
@@ -135,8 +145,8 @@
 #define grpc_lb_v1_Server_port_tag               2
 #define grpc_lb_v1_Server_load_balance_token_tag 3
 #define grpc_lb_v1_Server_drop_request_tag       4
-#define grpc_lb_v1_InitialLoadBalanceResponse_load_balancer_delegate_tag 2
-#define grpc_lb_v1_InitialLoadBalanceResponse_client_stats_report_interval_tag 3
+#define grpc_lb_v1_InitialLoadBalanceResponse_load_balancer_delegate_tag 1
+#define grpc_lb_v1_InitialLoadBalanceResponse_client_stats_report_interval_tag 2
 #define grpc_lb_v1_LoadBalanceRequest_initial_request_tag 1
 #define grpc_lb_v1_LoadBalanceRequest_client_stats_tag 2
 #define grpc_lb_v1_ServerList_servers_tag        1
@@ -161,7 +171,8 @@
 #define grpc_lb_v1_ClientStats_size              33
 #define grpc_lb_v1_LoadBalanceResponse_size      (98 + grpc_lb_v1_ServerList_size)
 #define grpc_lb_v1_InitialLoadBalanceResponse_size 90
-#define grpc_lb_v1_Server_size                   127
+/* grpc_lb_v1_ServerList_size depends on runtime parameters */
+#define grpc_lb_v1_Server_size                   98
 
 /* Message IDs (where set with "msgid" option) */
 #ifdef PB_MSGID
@@ -174,5 +185,6 @@
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
+/* @@protoc_insertion_point(eof) */
 
 #endif
diff --git a/src/core/ext/lb_policy/pick_first/pick_first.c b/src/core/ext/lb_policy/pick_first/pick_first.c
index 9decf70..961a0c9 100644
--- a/src/core/ext/lb_policy/pick_first/pick_first.c
+++ b/src/core/ext/lb_policy/pick_first/pick_first.c
@@ -128,7 +128,8 @@
 }
 
 static void pf_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
-                           grpc_connected_subchannel **target) {
+                           grpc_connected_subchannel **target,
+                           grpc_error *error) {
   pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
   pending_pick *pp;
   gpr_mu_lock(&p->mu);
@@ -140,8 +141,9 @@
       grpc_polling_entity_del_from_pollset_set(exec_ctx, pp->pollent,
                                                p->base.interested_parties);
       *target = NULL;
-      grpc_exec_ctx_sched(exec_ctx, pp->on_complete,
-                          GRPC_ERROR_CREATE("Pick Cancelled"), NULL);
+      grpc_exec_ctx_sched(
+          exec_ctx, pp->on_complete,
+          GRPC_ERROR_CREATE_REFERENCING("Pick Cancelled", &error, 1), NULL);
       gpr_free(pp);
     } else {
       pp->next = p->pending_picks;
@@ -150,11 +152,13 @@
     pp = next;
   }
   gpr_mu_unlock(&p->mu);
+  GRPC_ERROR_UNREF(error);
 }
 
 static void pf_cancel_picks(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
                             uint32_t initial_metadata_flags_mask,
-                            uint32_t initial_metadata_flags_eq) {
+                            uint32_t initial_metadata_flags_eq,
+                            grpc_error *error) {
   pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
   pending_pick *pp;
   gpr_mu_lock(&p->mu);
@@ -166,8 +170,9 @@
         initial_metadata_flags_eq) {
       grpc_polling_entity_del_from_pollset_set(exec_ctx, pp->pollent,
                                                p->base.interested_parties);
-      grpc_exec_ctx_sched(exec_ctx, pp->on_complete,
-                          GRPC_ERROR_CREATE("Pick Cancelled"), NULL);
+      grpc_exec_ctx_sched(
+          exec_ctx, pp->on_complete,
+          GRPC_ERROR_CREATE_REFERENCING("Pick Cancelled", &error, 1), NULL);
       gpr_free(pp);
     } else {
       pp->next = p->pending_picks;
@@ -176,6 +181,7 @@
     pp = next;
   }
   gpr_mu_unlock(&p->mu);
+  GRPC_ERROR_UNREF(error);
 }
 
 static void start_picking(grpc_exec_ctx *exec_ctx, pick_first_lb_policy *p) {
@@ -199,10 +205,8 @@
 }
 
 static int pf_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
-                   grpc_polling_entity *pollent,
-                   grpc_metadata_batch *initial_metadata,
-                   uint32_t initial_metadata_flags,
-                   grpc_connected_subchannel **target,
+                   const grpc_lb_policy_pick_args *pick_args,
+                   grpc_connected_subchannel **target, void **user_data,
                    grpc_closure *on_complete) {
   pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
   pending_pick *pp;
@@ -225,13 +229,13 @@
     if (!p->started_picking) {
       start_picking(exec_ctx, p);
     }
-    grpc_polling_entity_add_to_pollset_set(exec_ctx, pollent,
+    grpc_polling_entity_add_to_pollset_set(exec_ctx, pick_args->pollent,
                                            p->base.interested_parties);
     pp = gpr_malloc(sizeof(*pp));
     pp->next = p->pending_picks;
-    pp->pollent = pollent;
+    pp->pollent = pick_args->pollent;
     pp->target = target;
-    pp->initial_metadata_flags = initial_metadata_flags;
+    pp->initial_metadata_flags = pick_args->initial_metadata_flags;
     pp->on_complete = on_complete;
     p->pending_picks = pp;
     gpr_mu_unlock(&p->mu);
@@ -443,20 +447,37 @@
   GPR_ASSERT(args->addresses != NULL);
   GPR_ASSERT(args->client_channel_factory != NULL);
 
-  if (args->addresses->naddrs == 0) return NULL;
+  /* Find the number of backend addresses. We ignore balancer
+   * addresses, since we don't know how to handle them. */
+  size_t num_addrs = 0;
+  for (size_t i = 0; i < args->addresses->num_addresses; i++) {
+    if (!args->addresses->addresses[i].is_balancer) ++num_addrs;
+  }
+  if (num_addrs == 0) return NULL;
 
   pick_first_lb_policy *p = gpr_malloc(sizeof(*p));
   memset(p, 0, sizeof(*p));
 
-  p->subchannels =
-      gpr_malloc(sizeof(grpc_subchannel *) * args->addresses->naddrs);
-  memset(p->subchannels, 0, sizeof(*p->subchannels) * args->addresses->naddrs);
+  p->subchannels = gpr_malloc(sizeof(grpc_subchannel *) * num_addrs);
+  memset(p->subchannels, 0, sizeof(*p->subchannels) * num_addrs);
   grpc_subchannel_args sc_args;
   size_t subchannel_idx = 0;
-  for (size_t i = 0; i < args->addresses->naddrs; i++) {
+  for (size_t i = 0; i < args->addresses->num_addresses; i++) {
+    /* Skip balancer addresses, since we only know how to handle backends. */
+    if (args->addresses->addresses[i].is_balancer) continue;
+
+    if (args->addresses->addresses[i].user_data != NULL) {
+      gpr_log(GPR_ERROR,
+              "This LB policy doesn't support user data. It will be ignored");
+    }
+
     memset(&sc_args, 0, sizeof(grpc_subchannel_args));
-    sc_args.addr = (struct sockaddr *)(args->addresses->addrs[i].addr);
-    sc_args.addr_len = (size_t)args->addresses->addrs[i].len;
+    /* server_name will be copied as part of the subchannel creation. This makes
+     * the copying of args->server_name (a borrowed pointer) OK. */
+    sc_args.server_name = args->server_name;
+    sc_args.addr =
+        (struct sockaddr *)(&args->addresses->addresses[i].address.addr);
+    sc_args.addr_len = args->addresses->addresses[i].address.len;
 
     grpc_subchannel *subchannel = grpc_client_channel_factory_create_subchannel(
         exec_ctx, args->client_channel_factory, &sc_args);
diff --git a/src/core/ext/lb_policy/round_robin/round_robin.c b/src/core/ext/lb_policy/round_robin/round_robin.c
index 7bcf608..930fa86 100644
--- a/src/core/ext/lb_policy/round_robin/round_robin.c
+++ b/src/core/ext/lb_policy/round_robin/round_robin.c
@@ -66,6 +66,7 @@
 #include "src/core/ext/client_config/lb_policy_registry.h"
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/static_metadata.h"
 
 typedef struct round_robin_lb_policy round_robin_lb_policy;
 
@@ -76,15 +77,32 @@
  * Once a pick is available, \a target is updated and \a on_complete called. */
 typedef struct pending_pick {
   struct pending_pick *next;
+
+  /* polling entity for the pick()'s async notification */
   grpc_polling_entity *pollent;
+
+  /* output argument where to store the pick()ed user_data. It'll be NULL if no
+   * such data is present or there's an error (the definite test for errors is
+   * \a target being NULL). */
+  void **user_data;
+
+  /* bitmask passed to pick() and used for selective cancelling. See
+   * grpc_lb_policy_cancel_picks() */
   uint32_t initial_metadata_flags;
+
+  /* output argument where to store the pick()ed connected subchannel, or NULL
+   * upon error. */
   grpc_connected_subchannel **target;
+
+  /* to be invoked once the pick() has completed (regardless of success) */
   grpc_closure *on_complete;
 } pending_pick;
 
 /** List of subchannels in a connectivity READY state */
 typedef struct ready_list {
   grpc_subchannel *subchannel;
+  /* references namesake entry in subchannel_data */
+  void *user_data;
   struct ready_list *next;
   struct ready_list *prev;
 } ready_list;
@@ -102,12 +120,17 @@
   ready_list *ready_list_node;
   /** last observed connectivity */
   grpc_connectivity_state connectivity_state;
+  /** the subchannel's target user data */
+  void *user_data;
 } subchannel_data;
 
 struct round_robin_lb_policy {
   /** base policy: must be first */
   grpc_lb_policy base;
 
+  /** total number of addresses received at creation time */
+  size_t num_addresses;
+
   /** all our subchannels */
   size_t num_subchannels;
   subchannel_data **subchannels;
@@ -166,16 +189,19 @@
 
   if (grpc_lb_round_robin_trace) {
     gpr_log(GPR_DEBUG, "[READYLIST] ADVANCED LAST PICK. NOW AT NODE %p (SC %p)",
-            p->ready_list_last_pick, p->ready_list_last_pick->subchannel);
+            (void *)p->ready_list_last_pick,
+            (void *)p->ready_list_last_pick->subchannel);
   }
 }
 
 /** Prepends (relative to the root at p->ready_list) the connected subchannel \a
  * csc to the list of ready subchannels. */
 static ready_list *add_connected_sc_locked(round_robin_lb_policy *p,
-                                           grpc_subchannel *sc) {
+                                           subchannel_data *sd) {
   ready_list *new_elem = gpr_malloc(sizeof(ready_list));
-  new_elem->subchannel = sc;
+  memset(new_elem, 0, sizeof(ready_list));
+  new_elem->subchannel = sd->subchannel;
+  new_elem->user_data = sd->user_data;
   if (p->ready_list.prev == NULL) {
     /* first element */
     new_elem->next = &p->ready_list;
@@ -189,7 +215,8 @@
     p->ready_list.prev = new_elem;
   }
   if (grpc_lb_round_robin_trace) {
-    gpr_log(GPR_DEBUG, "[READYLIST] ADDING NODE %p (SC %p)", new_elem, sc);
+    gpr_log(GPR_DEBUG, "[READYLIST] ADDING NODE %p (Conn. SC %p)",
+            (void *)new_elem, (void *)sd->subchannel);
   }
   return new_elem;
 }
@@ -216,8 +243,8 @@
   }
 
   if (grpc_lb_round_robin_trace) {
-    gpr_log(GPR_DEBUG, "[READYLIST] REMOVED NODE %p (SC %p)", node,
-            node->subchannel);
+    gpr_log(GPR_DEBUG, "[READYLIST] REMOVED NODE %p (SC %p)", (void *)node,
+            (void *)node->subchannel);
   }
 
   node->next = NULL;
@@ -229,9 +256,8 @@
 
 static void rr_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
   round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
-  size_t i;
   ready_list *elem;
-  for (i = 0; i < p->num_subchannels; i++) {
+  for (size_t i = 0; i < p->num_subchannels; i++) {
     subchannel_data *sd = p->subchannels[i];
     GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, "round_robin");
     gpr_free(sd);
@@ -251,6 +277,7 @@
     gpr_free(elem);
     elem = tmp;
   }
+
   gpr_free(p);
 }
 
@@ -281,7 +308,8 @@
 }
 
 static void rr_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
-                           grpc_connected_subchannel **target) {
+                           grpc_connected_subchannel **target,
+                           grpc_error *error) {
   round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
   pending_pick *pp;
   gpr_mu_lock(&p->mu);
@@ -293,8 +321,9 @@
       grpc_polling_entity_del_from_pollset_set(exec_ctx, pp->pollent,
                                                p->base.interested_parties);
       *target = NULL;
-      grpc_exec_ctx_sched(exec_ctx, pp->on_complete, GRPC_ERROR_CANCELLED,
-                          NULL);
+      grpc_exec_ctx_sched(
+          exec_ctx, pp->on_complete,
+          GRPC_ERROR_CREATE_REFERENCING("Pick cancelled", &error, 1), NULL);
       gpr_free(pp);
     } else {
       pp->next = p->pending_picks;
@@ -303,11 +332,13 @@
     pp = next;
   }
   gpr_mu_unlock(&p->mu);
+  GRPC_ERROR_UNREF(error);
 }
 
 static void rr_cancel_picks(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
                             uint32_t initial_metadata_flags_mask,
-                            uint32_t initial_metadata_flags_eq) {
+                            uint32_t initial_metadata_flags_eq,
+                            grpc_error *error) {
   round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
   pending_pick *pp;
   gpr_mu_lock(&p->mu);
@@ -320,8 +351,9 @@
       grpc_polling_entity_del_from_pollset_set(exec_ctx, pp->pollent,
                                                p->base.interested_parties);
       *pp->target = NULL;
-      grpc_exec_ctx_sched(exec_ctx, pp->on_complete, GRPC_ERROR_CANCELLED,
-                          NULL);
+      grpc_exec_ctx_sched(
+          exec_ctx, pp->on_complete,
+          GRPC_ERROR_CREATE_REFERENCING("Pick cancelled", &error, 1), NULL);
       gpr_free(pp);
     } else {
       pp->next = p->pending_picks;
@@ -330,6 +362,7 @@
     pp = next;
   }
   gpr_mu_unlock(&p->mu);
+  GRPC_ERROR_UNREF(error);
 }
 
 static void start_picking(grpc_exec_ctx *exec_ctx, round_robin_lb_policy *p) {
@@ -337,7 +370,7 @@
   p->started_picking = 1;
 
   if (grpc_lb_round_robin_trace) {
-    gpr_log(GPR_DEBUG, "LB_POLICY: p=%p num_subchannels=%" PRIuPTR, p,
+    gpr_log(GPR_DEBUG, "LB_POLICY: p=%p num_subchannels=%" PRIuPTR, (void *)p,
             p->num_subchannels);
   }
 
@@ -361,38 +394,43 @@
 }
 
 static int rr_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
-                   grpc_polling_entity *pollent,
-                   grpc_metadata_batch *initial_metadata,
-                   uint32_t initial_metadata_flags,
-                   grpc_connected_subchannel **target,
+                   const grpc_lb_policy_pick_args *pick_args,
+                   grpc_connected_subchannel **target, void **user_data,
                    grpc_closure *on_complete) {
   round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
   pending_pick *pp;
   ready_list *selected;
   gpr_mu_lock(&p->mu);
   if ((selected = peek_next_connected_locked(p))) {
+    /* readily available, report right away */
     gpr_mu_unlock(&p->mu);
     *target = grpc_subchannel_get_connected_subchannel(selected->subchannel);
+
+    if (user_data != NULL) {
+      *user_data = selected->user_data;
+    }
     if (grpc_lb_round_robin_trace) {
       gpr_log(GPR_DEBUG,
-              "[RR PICK] TARGET <-- CONNECTED SUBCHANNEL %p (NODE %p)", *target,
-              selected);
+              "[RR PICK] TARGET <-- CONNECTED SUBCHANNEL %p (NODE %p)",
+              (void *)*target, (void *)selected);
     }
     /* only advance the last picked pointer if the selection was used */
     advance_last_picked_locked(p);
     return 1;
   } else {
+    /* no pick currently available. Save for later in list of pending picks */
     if (!p->started_picking) {
       start_picking(exec_ctx, p);
     }
-    grpc_polling_entity_add_to_pollset_set(exec_ctx, pollent,
+    grpc_polling_entity_add_to_pollset_set(exec_ctx, pick_args->pollent,
                                            p->base.interested_parties);
     pp = gpr_malloc(sizeof(*pp));
     pp->next = p->pending_picks;
-    pp->pollent = pollent;
+    pp->pollent = pick_args->pollent;
     pp->target = target;
     pp->on_complete = on_complete;
-    pp->initial_metadata_flags = initial_metadata_flags;
+    pp->initial_metadata_flags = pick_args->initial_metadata_flags;
+    pp->user_data = user_data;
     p->pending_picks = pp;
     gpr_mu_unlock(&p->mu);
     return 0;
@@ -421,7 +459,7 @@
                                     "connecting_ready");
         /* add the newly connected subchannel to the list of connected ones.
          * Note that it goes to the "end of the line". */
-        sd->ready_list_node = add_connected_sc_locked(p, sd->subchannel);
+        sd->ready_list_node = add_connected_sc_locked(p, sd);
         /* at this point we know there's at least one suitable subchannel. Go
          * ahead and pick one and notify the pending suitors in
          * p->pending_picks. This preemtively replicates rr_pick()'s actions. */
@@ -433,12 +471,16 @@
         }
         while ((pp = p->pending_picks)) {
           p->pending_picks = pp->next;
+
           *pp->target =
               grpc_subchannel_get_connected_subchannel(selected->subchannel);
+          if (pp->user_data != NULL) {
+            *pp->user_data = selected->user_data;
+          }
           if (grpc_lb_round_robin_trace) {
             gpr_log(GPR_DEBUG,
                     "[RR CONN CHANGED] TARGET <-- SUBCHANNEL %p (NODE %p)",
-                    selected->subchannel, selected);
+                    (void *)selected->subchannel, (void *)selected);
           }
           grpc_polling_entity_del_from_pollset_set(exec_ctx, pp->pollent,
                                                    p->base.interested_parties);
@@ -571,19 +613,34 @@
   GPR_ASSERT(args->addresses != NULL);
   GPR_ASSERT(args->client_channel_factory != NULL);
 
+  /* Find the number of backend addresses. We ignore balancer
+   * addresses, since we don't know how to handle them. */
+  size_t num_addrs = 0;
+  for (size_t i = 0; i < args->addresses->num_addresses; i++) {
+    if (!args->addresses->addresses[i].is_balancer) ++num_addrs;
+  }
+  if (num_addrs == 0) return NULL;
+
   round_robin_lb_policy *p = gpr_malloc(sizeof(*p));
   memset(p, 0, sizeof(*p));
 
-  p->subchannels =
-      gpr_malloc(sizeof(*p->subchannels) * args->addresses->naddrs);
-  memset(p->subchannels, 0, sizeof(*p->subchannels) * args->addresses->naddrs);
+  p->num_addresses = num_addrs;
+  p->subchannels = gpr_malloc(sizeof(*p->subchannels) * num_addrs);
+  memset(p->subchannels, 0, sizeof(*p->subchannels) * num_addrs);
 
   grpc_subchannel_args sc_args;
   size_t subchannel_idx = 0;
-  for (size_t i = 0; i < args->addresses->naddrs; i++) {
+  for (size_t i = 0; i < args->addresses->num_addresses; i++) {
+    /* Skip balancer addresses, since we only know how to handle backends. */
+    if (args->addresses->addresses[i].is_balancer) continue;
+
     memset(&sc_args, 0, sizeof(grpc_subchannel_args));
-    sc_args.addr = (struct sockaddr *)(args->addresses->addrs[i].addr);
-    sc_args.addr_len = (size_t)args->addresses->addrs[i].len;
+    /* server_name will be copied as part of the subchannel creation. This makes
+     * the copying of args->server_name (a borrowed pointer) OK. */
+    sc_args.server_name = args->server_name;
+    sc_args.addr =
+        (struct sockaddr *)(&args->addresses->addresses[i].address.addr);
+    sc_args.addr_len = args->addresses->addresses[i].address.len;
 
     grpc_subchannel *subchannel = grpc_client_channel_factory_create_subchannel(
         exec_ctx, args->client_channel_factory, &sc_args);
@@ -595,12 +652,14 @@
       sd->policy = p;
       sd->index = subchannel_idx;
       sd->subchannel = subchannel;
+      sd->user_data = args->addresses->addresses[i].user_data;
       ++subchannel_idx;
       grpc_closure_init(&sd->connectivity_changed_closure,
                         rr_connectivity_changed, sd);
     }
   }
   if (subchannel_idx == 0) {
+    /* couldn't create any subchannel. Bail out */
     gpr_free(p->subchannels);
     gpr_free(p);
     return NULL;
diff --git a/src/core/ext/load_reporting/load_reporting.c b/src/core/ext/load_reporting/load_reporting.c
index 9e4d326..df1ea0e 100644
--- a/src/core/ext/load_reporting/load_reporting.c
+++ b/src/core/ext/load_reporting/load_reporting.c
@@ -42,42 +42,12 @@
 #include "src/core/lib/channel/channel_stack_builder.h"
 #include "src/core/lib/surface/channel_init.h"
 
-struct grpc_load_reporting_config {
-  grpc_load_reporting_fn fn;
-  void *user_data;
-};
-
-grpc_load_reporting_config *grpc_load_reporting_config_create(
-    grpc_load_reporting_fn fn, void *user_data) {
-  GPR_ASSERT(fn != NULL);
-  grpc_load_reporting_config *lrc =
-      gpr_malloc(sizeof(grpc_load_reporting_config));
-  lrc->fn = fn;
-  lrc->user_data = user_data;
-  return lrc;
-}
-
-grpc_load_reporting_config *grpc_load_reporting_config_copy(
-    grpc_load_reporting_config *src) {
-  return grpc_load_reporting_config_create(src->fn, src->user_data);
-}
-
-void grpc_load_reporting_config_destroy(grpc_load_reporting_config *lrc) {
-  gpr_free(lrc);
-}
-
-void grpc_load_reporting_config_call(
-    grpc_load_reporting_config *lrc,
-    const grpc_load_reporting_call_data *call_data) {
-  lrc->fn(call_data, lrc->user_data);
-}
-
 static bool is_load_reporting_enabled(const grpc_channel_args *a) {
   if (a == NULL) return false;
   for (size_t i = 0; i < a->num_args; i++) {
     if (0 == strcmp(a->args[i].key, GRPC_ARG_ENABLE_LOAD_REPORTING)) {
-      return a->args[i].type == GRPC_ARG_POINTER &&
-             a->args[i].value.pointer.p != NULL;
+      return a->args[i].type == GRPC_ARG_INTEGER &&
+             a->args[i].value.integer != 0;
     }
   }
   return false;
@@ -94,37 +64,17 @@
   return true;
 }
 
-static void lrd_arg_destroy(void *p) { grpc_load_reporting_config_destroy(p); }
-
-static void *lrd_arg_copy(void *p) {
-  return grpc_load_reporting_config_copy(p);
-}
-
-static int lrd_arg_cmp(void *a, void *b) {
-  grpc_load_reporting_config *lhs = a;
-  grpc_load_reporting_config *rhs = b;
-  return !(lhs->fn == rhs->fn && lhs->user_data == rhs->user_data);
-}
-
-static const grpc_arg_pointer_vtable lrd_ptr_vtable = {
-    lrd_arg_copy, lrd_arg_destroy, lrd_arg_cmp};
-
-grpc_arg grpc_load_reporting_config_create_arg(
-    grpc_load_reporting_config *lrc) {
+grpc_arg grpc_load_reporting_enable_arg() {
   grpc_arg arg;
-  arg.type = GRPC_ARG_POINTER;
+  arg.type = GRPC_ARG_INTEGER;
   arg.key = GRPC_ARG_ENABLE_LOAD_REPORTING;
-  arg.value.pointer.p = lrc;
-  arg.value.pointer.vtable = &lrd_ptr_vtable;
+  arg.value.integer = 1;
   return arg;
 }
 
 /* Plugin registration */
 
 void grpc_load_reporting_plugin_init(void) {
-  grpc_channel_init_register_stage(GRPC_CLIENT_CHANNEL, INT_MAX,
-                                   maybe_add_load_reporting_filter,
-                                   (void *)&grpc_load_reporting_filter);
   grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX,
                                    maybe_add_load_reporting_filter,
                                    (void *)&grpc_load_reporting_filter);
diff --git a/src/core/ext/load_reporting/load_reporting.h b/src/core/ext/load_reporting/load_reporting.h
index 316cd89..e37817d 100644
--- a/src/core/ext/load_reporting/load_reporting.h
+++ b/src/core/ext/load_reporting/load_reporting.h
@@ -34,42 +34,47 @@
 #ifndef GRPC_CORE_EXT_LOAD_REPORTING_LOAD_REPORTING_H
 #define GRPC_CORE_EXT_LOAD_REPORTING_LOAD_REPORTING_H
 
-#include "src/core/lib/iomgr/closure.h"
-#include "src/core/lib/surface/call.h"
+#include <grpc/impl/codegen/grpc_types.h>
+#include "src/core/lib/channel/channel_stack.h"
 
-typedef struct grpc_load_reporting_config grpc_load_reporting_config;
+/** Metadata key for initial metadata coming from clients */
+/* TODO(dgq): change to the final value TBD */
+#define GRPC_LOAD_REPORTING_INITIAL_MD_KEY "load-reporting-initial"
 
-/** Call information to be passed to the provided load reporting function upon
- * completion of the call */
+/** Metadata key for trailing metadata from servers */
+/* TODO(dgq): change to the final value TBD */
+#define GRPC_LOAD_REPORTING_TRAILING_MD_KEY "load-reporting-trailing"
+
+/** Identifiers for the invocation point of the users LR callback */
+typedef enum grpc_load_reporting_source {
+  GRPC_LR_POINT_UNKNOWN = 0,
+  GRPC_LR_POINT_CHANNEL_CREATION,
+  GRPC_LR_POINT_CHANNEL_DESTRUCTION,
+  GRPC_LR_POINT_CALL_CREATION,
+  GRPC_LR_POINT_CALL_DESTRUCTION
+} grpc_load_reporting_source;
+
+/** Call information to be passed to the provided LR callback. */
 typedef struct grpc_load_reporting_call_data {
-  const grpc_call_stats *stats;   /**< Stats for the call */
-  const char *trailing_md_string; /**< LR trailing metadata info */
+  const grpc_load_reporting_source source; /**< point of last data update. */
+
+  /** Unique identifier for the channel associated with the data */
+  intptr_t channel_id;
+
+  /** Unique identifier for the call associated with the data. If the call
+   * hasn't been created yet, it'll have a value of zero. */
+  intptr_t call_id;
+
+  /** Only valid when \a source is \a GRPC_LR_POINT_CALL_DESTRUCTION, that is,
+   * once the call has completed */
+  const grpc_call_final_info *final_info;
+
+  const char *initial_md_string;  /**< value string for LR's initial md key */
+  const char *trailing_md_string; /**< value string for LR's trailing md key */
+  const char *method_name;        /**< Corresponds to :path header */
 } grpc_load_reporting_call_data;
 
-/** Custom function to be called by the load reporting filter. */
-typedef void (*grpc_load_reporting_fn)(
-    const grpc_load_reporting_call_data *call_data, void *user_data);
-
-/** Register \a fn as the function to be invoked by the load reporting filter.
- * \a fn will be invoked at the beginning and at the end of the call.
- *
- * For the first invocation, \a fn's first argument
- * (grpc_load_reporting_call_data*) will be NULL. \a user_data is always passed
- * as-is. */
-grpc_load_reporting_config *grpc_load_reporting_config_create(
-    grpc_load_reporting_fn fn, void *user_data);
-
-grpc_load_reporting_config *grpc_load_reporting_config_copy(
-    grpc_load_reporting_config *src);
-
-void grpc_load_reporting_config_destroy(grpc_load_reporting_config *lrc);
-
-/** Invoke the function registered by \a grpc_load_reporting_init. */
-void grpc_load_reporting_config_call(
-    grpc_load_reporting_config *lrc,
-    const grpc_load_reporting_call_data *call_data);
-
 /** Return a \a grpc_arg enabling load reporting */
-grpc_arg grpc_load_reporting_config_create_arg(grpc_load_reporting_config *lrc);
+grpc_arg grpc_load_reporting_enable_arg();
 
 #endif /* GRPC_CORE_EXT_LOAD_REPORTING_LOAD_REPORTING_H */
diff --git a/src/core/ext/load_reporting/load_reporting_filter.c b/src/core/ext/load_reporting/load_reporting_filter.c
index f372f88..394f0cb 100644
--- a/src/core/ext/load_reporting/load_reporting_filter.c
+++ b/src/core/ext/load_reporting/load_reporting_filter.c
@@ -31,6 +31,7 @@
  *
  */
 
+#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
@@ -42,38 +43,112 @@
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/transport/static_metadata.h"
 
-typedef struct call_data { const char *trailing_md_string; } call_data;
+typedef struct call_data {
+  intptr_t id; /**< an id unique to the call */
+  char *trailing_md_string;
+  char *initial_md_string;
+  const char *service_method;
+
+  /* stores the recv_initial_metadata op's ready closure, which we wrap with our
+   * own (on_initial_md_ready) in order to capture the incoming initial metadata
+   * */
+  grpc_closure *ops_recv_initial_metadata_ready;
+
+  /* to get notified of the availability of the incoming initial metadata. */
+  grpc_closure on_initial_md_ready;
+  grpc_metadata_batch *recv_initial_metadata;
+} call_data;
+
 typedef struct channel_data {
-  gpr_mu mu;
-  grpc_load_reporting_config *lrc;
+  intptr_t id; /**< an id unique to the channel */
 } channel_data;
 
-static void invoke_lr_fn_locked(grpc_load_reporting_config *lrc,
-                                grpc_load_reporting_call_data *lr_call_data) {
-  GPR_TIMER_BEGIN("load_reporting_config_fn", 0);
-  grpc_load_reporting_config_call(lrc, lr_call_data);
-  GPR_TIMER_END("load_reporting_config_fn", 0);
+typedef struct {
+  grpc_call_element *elem;
+  grpc_exec_ctx *exec_ctx;
+} recv_md_filter_args;
+
+static grpc_mdelem *recv_md_filter(void *user_data, grpc_mdelem *md) {
+  recv_md_filter_args *a = user_data;
+  grpc_call_element *elem = a->elem;
+  call_data *calld = elem->call_data;
+
+  if (md->key == GRPC_MDSTR_PATH) {
+    calld->service_method = grpc_mdstr_as_c_string(md->value);
+  } else if (md->key == GRPC_MDSTR_LOAD_REPORTING_INITIAL) {
+    calld->initial_md_string = gpr_strdup(grpc_mdstr_as_c_string(md->value));
+    return NULL;
+  }
+
+  return md;
+}
+
+static void on_initial_md_ready(grpc_exec_ctx *exec_ctx, void *user_data,
+                                grpc_error *err) {
+  grpc_call_element *elem = user_data;
+  call_data *calld = elem->call_data;
+
+  if (err == GRPC_ERROR_NONE) {
+    recv_md_filter_args a;
+    a.elem = elem;
+    a.exec_ctx = exec_ctx;
+    grpc_metadata_batch_filter(calld->recv_initial_metadata, recv_md_filter,
+                               &a);
+    if (calld->service_method == NULL) {
+      err =
+          grpc_error_add_child(err, GRPC_ERROR_CREATE("Missing :path header"));
+    }
+  } else {
+    GRPC_ERROR_REF(err);
+  }
+  calld->ops_recv_initial_metadata_ready->cb(
+      exec_ctx, calld->ops_recv_initial_metadata_ready->cb_arg, err);
+  GRPC_ERROR_UNREF(err);
 }
 
 /* Constructor for call_data */
-static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {
+static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  grpc_call_element_args *args) {
   call_data *calld = elem->call_data;
   memset(calld, 0, sizeof(call_data));
+
+  calld->id = (intptr_t)args->call_stack;
+  grpc_closure_init(&calld->on_initial_md_ready, on_initial_md_ready, elem);
+
+  /* TODO(dgq): do something with the data
+  channel_data *chand = elem->channel_data;
+  grpc_load_reporting_call_data lr_call_data = {GRPC_LR_POINT_CALL_CREATION,
+                                                (intptr_t)chand->id,
+                                                (intptr_t)calld->id,
+                                                NULL,
+                                                NULL,
+                                                NULL,
+                                                NULL};
+  */
+
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {
-  channel_data *chand = elem->channel_data;
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {
   call_data *calld = elem->call_data;
 
-  grpc_load_reporting_call_data lr_call_data = {stats,
-                                                calld->trailing_md_string};
+  /* TODO(dgq): do something with the data
+  channel_data *chand = elem->channel_data;
+  grpc_load_reporting_call_data lr_call_data = {GRPC_LR_POINT_CALL_DESTRUCTION,
+                                                (intptr_t)chand->id,
+                                                (intptr_t)calld->id,
+                                                final_info,
+                                                calld->initial_md_string,
+                                                calld->trailing_md_string,
+                                                calld->service_method};
+  */
 
-  gpr_mu_lock(&chand->mu);
-  invoke_lr_fn_locked(chand->lrc, &lr_call_data);
-  gpr_mu_unlock(&chand->mu);
+  gpr_free(calld->initial_md_string);
+  gpr_free(calld->trailing_md_string);
 }
 
 /* Constructor for channel_data */
@@ -85,37 +160,40 @@
   channel_data *chand = elem->channel_data;
   memset(chand, 0, sizeof(channel_data));
 
-  gpr_mu_init(&chand->mu);
-  for (size_t i = 0; i < args->channel_args->num_args; i++) {
-    if (0 == strcmp(args->channel_args->args[i].key,
-                    GRPC_ARG_ENABLE_LOAD_REPORTING)) {
-      grpc_load_reporting_config *arg_lrc =
-          args->channel_args->args[i].value.pointer.p;
-      chand->lrc = grpc_load_reporting_config_copy(arg_lrc);
-      GPR_ASSERT(chand->lrc != NULL);
-      break;
-    }
-  }
-  GPR_ASSERT(chand->lrc != NULL); /* arg actually found */
+  chand->id = (intptr_t)args->channel_stack;
 
-  gpr_mu_lock(&chand->mu);
-  invoke_lr_fn_locked(chand->lrc, NULL);
-  gpr_mu_unlock(&chand->mu);
+  /* TODO(dgq): do something with the data
+  grpc_load_reporting_call_data lr_call_data = {GRPC_LR_POINT_CHANNEL_CREATION,
+                                                (intptr_t)chand,
+                                                0,
+                                                NULL,
+                                                NULL,
+                                                NULL,
+                                                NULL};
+                                                */
 }
 
 /* Destructor for channel data */
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {
+  /* TODO(dgq): do something with the data
   channel_data *chand = elem->channel_data;
-  gpr_mu_destroy(&chand->mu);
-  grpc_load_reporting_config_destroy(chand->lrc);
+  grpc_load_reporting_call_data lr_call_data = {
+      GRPC_LR_POINT_CHANNEL_DESTRUCTION,
+      (intptr_t)chand->id,
+      0,
+      NULL,
+      NULL,
+      NULL,
+      NULL};
+  */
 }
 
 static grpc_mdelem *lr_trailing_md_filter(void *user_data, grpc_mdelem *md) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
 
-  if (md->key == GRPC_MDSTR_LOAD_REPORTING) {
+  if (md->key == GRPC_MDSTR_LOAD_REPORTING_TRAILING) {
     calld->trailing_md_string = gpr_strdup(grpc_mdstr_as_c_string(md->value));
     return NULL;
   }
@@ -127,8 +205,14 @@
                                          grpc_call_element *elem,
                                          grpc_transport_stream_op *op) {
   GPR_TIMER_BEGIN("lr_start_transport_stream_op", 0);
+  call_data *calld = elem->call_data;
 
-  if (op->send_trailing_metadata) {
+  if (op->recv_initial_metadata) {
+    calld->recv_initial_metadata = op->recv_initial_metadata;
+    /* substitute our callback for the higher callback */
+    calld->ops_recv_initial_metadata_ready = op->recv_initial_metadata_ready;
+    op->recv_initial_metadata_ready = &calld->on_initial_md_ready;
+  } else if (op->send_trailing_metadata) {
     grpc_metadata_batch_filter(op->send_trailing_metadata,
                                lr_trailing_md_filter, elem);
   }
diff --git a/src/core/ext/load_reporting/load_reporting_filter.h b/src/core/ext/load_reporting/load_reporting_filter.h
index f69cd6f..160ed32 100644
--- a/src/core/ext/load_reporting/load_reporting_filter.h
+++ b/src/core/ext/load_reporting/load_reporting_filter.h
@@ -34,6 +34,7 @@
 #ifndef GRPC_CORE_EXT_LOAD_REPORTING_LOAD_REPORTING_FILTER_H
 #define GRPC_CORE_EXT_LOAD_REPORTING_LOAD_REPORTING_FILTER_H
 
+#include "src/core/ext/load_reporting/load_reporting.h"
 #include "src/core/lib/channel/channel_stack.h"
 
 extern const grpc_channel_filter grpc_load_reporting_filter;
diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c
index 31ac968..fa33ffd 100644
--- a/src/core/ext/resolver/dns/native/dns_resolver.c
+++ b/src/core/ext/resolver/dns/native/dns_resolver.c
@@ -37,6 +37,7 @@
 #include <grpc/support/host_port.h>
 #include <grpc/support/string_util.h>
 
+#include "src/core/ext/client_config/http_connect_handshaker.h"
 #include "src/core/ext/client_config/lb_policy_registry.h"
 #include "src/core/ext/client_config/resolver_registry.h"
 #include "src/core/lib/iomgr/resolve_address.h"
@@ -52,31 +53,27 @@
 typedef struct {
   /** base class: must be first */
   grpc_resolver base;
-  /** refcount */
-  gpr_refcount refs;
-  /** name to resolve */
-  char *name;
+  /** target name */
+  char *target_name;
+  /** name to resolve (usually the same as target_name) */
+  char *name_to_resolve;
   /** default port to use */
   char *default_port;
-  /** subchannel factory */
-  grpc_client_channel_factory *client_channel_factory;
-  /** load balancing policy name */
-  char *lb_policy_name;
 
   /** mutex guarding the rest of the state */
   gpr_mu mu;
   /** are we currently resolving? */
-  int resolving;
-  /** which version of resolved_config have we published? */
+  bool resolving;
+  /** which version of the result have we published? */
   int published_version;
-  /** which version of resolved_config is current? */
+  /** which version of the result is current? */
   int resolved_version;
   /** pending next completion, or NULL */
   grpc_closure *next_completion;
-  /** target config address for next completion */
-  grpc_client_config **target_config;
-  /** current (fully resolved) config */
-  grpc_client_config *resolved_config;
+  /** target result address for next completion */
+  grpc_resolver_result **target_result;
+  /** current (fully resolved) result */
+  grpc_resolver_result *resolved_result;
   /** retry timer */
   bool have_retry_timer;
   grpc_timer retry_timer;
@@ -97,7 +94,7 @@
 static void dns_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r);
 static void dns_channel_saw_error(grpc_exec_ctx *exec_ctx, grpc_resolver *r);
 static void dns_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r,
-                     grpc_client_config **target_config,
+                     grpc_resolver_result **target_result,
                      grpc_closure *on_complete);
 
 static const grpc_resolver_vtable dns_resolver_vtable = {
@@ -110,7 +107,7 @@
     grpc_timer_cancel(exec_ctx, &r->retry_timer);
   }
   if (r->next_completion != NULL) {
-    *r->target_config = NULL;
+    *r->target_result = NULL;
     grpc_exec_ctx_sched(exec_ctx, r->next_completion,
                         GRPC_ERROR_CREATE("Resolver Shutdown"), NULL);
     r->next_completion = NULL;
@@ -130,13 +127,13 @@
 }
 
 static void dns_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver,
-                     grpc_client_config **target_config,
+                     grpc_resolver_result **target_result,
                      grpc_closure *on_complete) {
   dns_resolver *r = (dns_resolver *)resolver;
   gpr_mu_lock(&r->mu);
   GPR_ASSERT(!r->next_completion);
   r->next_completion = on_complete;
-  r->target_config = target_config;
+  r->target_result = target_result;
   if (r->resolved_version == 0 && !r->resolving) {
     gpr_backoff_reset(&r->backoff_state);
     dns_start_resolving_locked(exec_ctx, r);
@@ -165,25 +162,22 @@
 static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg,
                             grpc_error *error) {
   dns_resolver *r = arg;
-  grpc_client_config *config = NULL;
-  grpc_lb_policy *lb_policy;
+  grpc_resolver_result *result = NULL;
   gpr_mu_lock(&r->mu);
   GPR_ASSERT(r->resolving);
-  r->resolving = 0;
-  grpc_resolved_addresses *addresses = r->addresses;
-  if (addresses != NULL) {
-    grpc_lb_policy_args lb_policy_args;
-    config = grpc_client_config_create();
-    memset(&lb_policy_args, 0, sizeof(lb_policy_args));
-    lb_policy_args.addresses = addresses;
-    lb_policy_args.client_channel_factory = r->client_channel_factory;
-    lb_policy =
-        grpc_lb_policy_create(exec_ctx, r->lb_policy_name, &lb_policy_args);
-    if (lb_policy != NULL) {
-      grpc_client_config_set_lb_policy(config, lb_policy);
-      GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "construction");
+  r->resolving = false;
+  if (r->addresses != NULL) {
+    grpc_lb_addresses *addresses =
+        grpc_lb_addresses_create(r->addresses->naddrs);
+    for (size_t i = 0; i < r->addresses->naddrs; ++i) {
+      grpc_lb_addresses_set_address(
+          addresses, i, &r->addresses->addrs[i].addr,
+          r->addresses->addrs[i].len, false /* is_balancer */,
+          NULL /* balancer_name */, NULL /* user_data */);
     }
-    grpc_resolved_addresses_destroy(addresses);
+    grpc_resolved_addresses_destroy(r->addresses);
+    result = grpc_resolver_result_create(r->target_name, addresses,
+                                         NULL /* lb_policy_name */, NULL);
   } else {
     gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
     gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now);
@@ -203,10 +197,10 @@
     grpc_timer_init(exec_ctx, &r->retry_timer, next_try, dns_on_retry_timer, r,
                     now);
   }
-  if (r->resolved_config) {
-    grpc_client_config_unref(exec_ctx, r->resolved_config);
+  if (r->resolved_result) {
+    grpc_resolver_result_unref(exec_ctx, r->resolved_result);
   }
-  r->resolved_config = config;
+  r->resolved_result = result;
   r->resolved_version++;
   dns_maybe_finish_next_locked(exec_ctx, r);
   gpr_mu_unlock(&r->mu);
@@ -218,9 +212,9 @@
                                        dns_resolver *r) {
   GRPC_RESOLVER_REF(&r->base, "dns-resolving");
   GPR_ASSERT(!r->resolving);
-  r->resolving = 1;
+  r->resolving = true;
   r->addresses = NULL;
-  grpc_resolve_address(exec_ctx, r->name, r->default_port,
+  grpc_resolve_address(exec_ctx, r->name_to_resolve, r->default_port,
                        grpc_closure_create(dns_on_resolved, r), &r->addresses);
 }
 
@@ -228,9 +222,9 @@
                                          dns_resolver *r) {
   if (r->next_completion != NULL &&
       r->resolved_version != r->published_version) {
-    *r->target_config = r->resolved_config;
-    if (r->resolved_config) {
-      grpc_client_config_ref(r->resolved_config);
+    *r->target_result = r->resolved_result;
+    if (r->resolved_result) {
+      grpc_resolver_result_ref(r->resolved_result);
     }
     grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
     r->next_completion = NULL;
@@ -241,41 +235,36 @@
 static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) {
   dns_resolver *r = (dns_resolver *)gr;
   gpr_mu_destroy(&r->mu);
-  if (r->resolved_config) {
-    grpc_client_config_unref(exec_ctx, r->resolved_config);
+  if (r->resolved_result) {
+    grpc_resolver_result_unref(exec_ctx, r->resolved_result);
   }
-  grpc_client_channel_factory_unref(exec_ctx, r->client_channel_factory);
-  gpr_free(r->name);
+  gpr_free(r->target_name);
+  gpr_free(r->name_to_resolve);
   gpr_free(r->default_port);
-  gpr_free(r->lb_policy_name);
   gpr_free(r);
 }
 
 static grpc_resolver *dns_create(grpc_resolver_args *args,
-                                 const char *default_port,
-                                 const char *lb_policy_name) {
-  dns_resolver *r;
-  const char *path = args->uri->path;
-
+                                 const char *default_port) {
   if (0 != strcmp(args->uri->authority, "")) {
     gpr_log(GPR_ERROR, "authority based dns uri's not supported");
     return NULL;
   }
-
+  // Get name from args.
+  const char *path = args->uri->path;
   if (path[0] == '/') ++path;
-
-  r = gpr_malloc(sizeof(dns_resolver));
+  // Get proxy name, if any.
+  char *proxy_name = grpc_get_http_proxy_server();
+  // Create resolver.
+  dns_resolver *r = gpr_malloc(sizeof(dns_resolver));
   memset(r, 0, sizeof(*r));
-  gpr_ref_init(&r->refs, 1);
   gpr_mu_init(&r->mu);
   grpc_resolver_init(&r->base, &dns_resolver_vtable);
-  r->name = gpr_strdup(path);
+  r->target_name = gpr_strdup(path);
+  r->name_to_resolve = proxy_name == NULL ? gpr_strdup(path) : proxy_name;
   r->default_port = gpr_strdup(default_port);
-  r->client_channel_factory = args->client_channel_factory;
   gpr_backoff_init(&r->backoff_state, BACKOFF_MULTIPLIER, BACKOFF_JITTER,
                    BACKOFF_MIN_SECONDS * 1000, BACKOFF_MAX_SECONDS * 1000);
-  grpc_client_channel_factory_ref(r->client_channel_factory);
-  r->lb_policy_name = gpr_strdup(lb_policy_name);
   return &r->base;
 }
 
@@ -289,7 +278,7 @@
 
 static grpc_resolver *dns_factory_create_resolver(
     grpc_resolver_factory *factory, grpc_resolver_args *args) {
-  return dns_create(args, "https", "pick_first");
+  return dns_create(args, "https");
 }
 
 static char *dns_factory_get_default_host_name(grpc_resolver_factory *factory,
diff --git a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
index 1f7cce2..5a7a32d 100644
--- a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
+++ b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
@@ -40,7 +40,6 @@
 #include <grpc/support/port_platform.h>
 #include <grpc/support/string_util.h>
 
-#include "src/core/ext/client_config/lb_policy_registry.h"
 #include "src/core/ext/client_config/parse_address.h"
 #include "src/core/ext/client_config/resolver_registry.h"
 #include "src/core/lib/iomgr/resolve_address.h"
@@ -50,24 +49,18 @@
 typedef struct {
   /** base class: must be first */
   grpc_resolver base;
-  /** refcount */
-  gpr_refcount refs;
-  /** subchannel factory */
-  grpc_client_channel_factory *client_channel_factory;
-  /** load balancing policy name */
-  char *lb_policy_name;
-
+  /** the path component of the uri passed in */
+  char *target_name;
   /** the addresses that we've 'resolved' */
-  grpc_resolved_addresses *addresses;
-
+  grpc_lb_addresses *addresses;
   /** mutex guarding the rest of the state */
   gpr_mu mu;
   /** have we published? */
-  int published;
+  bool published;
   /** pending next completion, or NULL */
   grpc_closure *next_completion;
-  /** target config address for next completion */
-  grpc_client_config **target_config;
+  /** target result address for next completion */
+  grpc_resolver_result **target_result;
 } sockaddr_resolver;
 
 static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r);
@@ -79,7 +72,7 @@
 static void sockaddr_channel_saw_error(grpc_exec_ctx *exec_ctx,
                                        grpc_resolver *r);
 static void sockaddr_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r,
-                          grpc_client_config **target_config,
+                          grpc_resolver_result **target_result,
                           grpc_closure *on_complete);
 
 static const grpc_resolver_vtable sockaddr_resolver_vtable = {
@@ -91,7 +84,7 @@
   sockaddr_resolver *r = (sockaddr_resolver *)resolver;
   gpr_mu_lock(&r->mu);
   if (r->next_completion != NULL) {
-    *r->target_config = NULL;
+    *r->target_result = NULL;
     grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
     r->next_completion = NULL;
   }
@@ -102,19 +95,19 @@
                                        grpc_resolver *resolver) {
   sockaddr_resolver *r = (sockaddr_resolver *)resolver;
   gpr_mu_lock(&r->mu);
-  r->published = 0;
+  r->published = false;
   sockaddr_maybe_finish_next_locked(exec_ctx, r);
   gpr_mu_unlock(&r->mu);
 }
 
 static void sockaddr_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver,
-                          grpc_client_config **target_config,
+                          grpc_resolver_result **target_result,
                           grpc_closure *on_complete) {
   sockaddr_resolver *r = (sockaddr_resolver *)resolver;
   gpr_mu_lock(&r->mu);
   GPR_ASSERT(!r->next_completion);
   r->next_completion = on_complete;
-  r->target_config = target_config;
+  r->target_result = target_result;
   sockaddr_maybe_finish_next_locked(exec_ctx, r);
   gpr_mu_unlock(&r->mu);
 }
@@ -122,17 +115,11 @@
 static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx,
                                               sockaddr_resolver *r) {
   if (r->next_completion != NULL && !r->published) {
-    grpc_client_config *cfg = grpc_client_config_create();
-    grpc_lb_policy_args lb_policy_args;
-    memset(&lb_policy_args, 0, sizeof(lb_policy_args));
-    lb_policy_args.addresses = r->addresses;
-    lb_policy_args.client_channel_factory = r->client_channel_factory;
-    grpc_lb_policy *lb_policy =
-        grpc_lb_policy_create(exec_ctx, r->lb_policy_name, &lb_policy_args);
-    grpc_client_config_set_lb_policy(cfg, lb_policy);
-    GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "sockaddr");
-    r->published = 1;
-    *r->target_config = cfg;
+    r->published = true;
+    *r->target_result = grpc_resolver_result_create(
+        r->target_name,
+        grpc_lb_addresses_copy(r->addresses, NULL /* user_data_copy */),
+        NULL /* lb_policy_name */, NULL);
     grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
     r->next_completion = NULL;
   }
@@ -141,9 +128,8 @@
 static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) {
   sockaddr_resolver *r = (sockaddr_resolver *)gr;
   gpr_mu_destroy(&r->mu);
-  grpc_client_channel_factory_unref(exec_ctx, r->client_channel_factory);
-  grpc_resolved_addresses_destroy(r->addresses);
-  gpr_free(r->lb_policy_name);
+  grpc_lb_addresses_destroy(r->addresses, NULL /* user_data_destroy */);
+  gpr_free(r->target_name);
   gpr_free(r);
 }
 
@@ -172,83 +158,49 @@
 
 static void do_nothing(void *ignored) {}
 
-static grpc_resolver *sockaddr_create(
-    grpc_resolver_args *args, const char *default_lb_policy_name,
-    int parse(grpc_uri *uri, struct sockaddr_storage *dst, size_t *len)) {
-  int errors_found = 0; /* GPR_FALSE */
-  sockaddr_resolver *r;
-  gpr_slice path_slice;
-  gpr_slice_buffer path_parts;
-
+static grpc_resolver *sockaddr_create(grpc_resolver_args *args,
+                                      int parse(grpc_uri *uri,
+                                                struct sockaddr_storage *dst,
+                                                size_t *len)) {
   if (0 != strcmp(args->uri->authority, "")) {
     gpr_log(GPR_ERROR, "authority based uri's not supported by the %s scheme",
             args->uri->scheme);
     return NULL;
   }
-
-  r = gpr_malloc(sizeof(sockaddr_resolver));
-  memset(r, 0, sizeof(*r));
-
-  r->lb_policy_name =
-      gpr_strdup(grpc_uri_get_query_arg(args->uri, "lb_policy"));
-  const char *lb_enabled_qpart =
-      grpc_uri_get_query_arg(args->uri, "lb_enabled");
-  /* anything other than "0" is interpreted as true */
-  const bool lb_enabled =
-      (lb_enabled_qpart != NULL && (strcmp("0", lb_enabled_qpart) != 0));
-
-  if (r->lb_policy_name != NULL && strcmp("grpclb", r->lb_policy_name) == 0 &&
-      !lb_enabled) {
-    /* we want grpclb but the "resolved" addresses aren't LB enabled. Bail
-     * out, as this is meant mostly for tests. */
-    gpr_log(GPR_ERROR,
-            "Requested 'grpclb' LB policy but resolved addresses don't "
-            "support load balancing.");
-    abort();
-  }
-
-  if (r->lb_policy_name == NULL) {
-    r->lb_policy_name = gpr_strdup(default_lb_policy_name);
-  }
-
-  path_slice =
+  /* Construct addresses. */
+  gpr_slice path_slice =
       gpr_slice_new(args->uri->path, strlen(args->uri->path), do_nothing);
+  gpr_slice_buffer path_parts;
   gpr_slice_buffer_init(&path_parts);
-
   gpr_slice_split(path_slice, ",", &path_parts);
-  r->addresses = gpr_malloc(sizeof(grpc_resolved_addresses));
-  r->addresses->naddrs = path_parts.count;
-  r->addresses->addrs =
-      gpr_malloc(sizeof(grpc_resolved_address) * r->addresses->naddrs);
-
-  for (size_t i = 0; i < r->addresses->naddrs; i++) {
+  grpc_lb_addresses *addresses = grpc_lb_addresses_create(path_parts.count);
+  bool errors_found = false;
+  for (size_t i = 0; i < addresses->num_addresses; i++) {
     grpc_uri ith_uri = *args->uri;
     char *part_str = gpr_dump_slice(path_parts.slices[i], GPR_DUMP_ASCII);
     ith_uri.path = part_str;
-    if (!parse(&ith_uri,
-               (struct sockaddr_storage *)(&r->addresses->addrs[i].addr),
-               &r->addresses->addrs[i].len)) {
-      errors_found = 1; /* GPR_TRUE */
+    if (!parse(
+            &ith_uri,
+            (struct sockaddr_storage *)(&addresses->addresses[i].address.addr),
+            &addresses->addresses[i].address.len)) {
+      errors_found = true;
     }
     gpr_free(part_str);
     if (errors_found) break;
   }
-
   gpr_slice_buffer_destroy(&path_parts);
   gpr_slice_unref(path_slice);
   if (errors_found) {
-    gpr_free(r->lb_policy_name);
-    grpc_resolved_addresses_destroy(r->addresses);
-    gpr_free(r);
+    grpc_lb_addresses_destroy(addresses, NULL /* user_data_destroy */);
     return NULL;
   }
-
-  gpr_ref_init(&r->refs, 1);
+  /* Instantiate resolver. */
+  sockaddr_resolver *r = gpr_malloc(sizeof(sockaddr_resolver));
+  memset(r, 0, sizeof(*r));
+  r->target_name = gpr_strdup(args->uri->path);
+  r->addresses = addresses;
   gpr_mu_init(&r->mu);
   grpc_resolver_init(&r->base, &sockaddr_resolver_vtable);
-  r->client_channel_factory = args->client_channel_factory;
-  grpc_client_channel_factory_ref(r->client_channel_factory);
-
   return &r->base;
 }
 
@@ -263,7 +215,7 @@
 #define DECL_FACTORY(name)                                                  \
   static grpc_resolver *name##_factory_create_resolver(                     \
       grpc_resolver_factory *factory, grpc_resolver_args *args) {           \
-    return sockaddr_create(args, "pick_first", parse_##name);               \
+    return sockaddr_create(args, parse_##name);                             \
   }                                                                         \
   static const grpc_resolver_factory_vtable name##_factory_vtable = {       \
       sockaddr_factory_ref, sockaddr_factory_unref,                         \
diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.c b/src/core/ext/transport/chttp2/client/insecure/channel_create.c
index 6f68555..c2b5956 100644
--- a/src/core/ext/transport/chttp2/client/insecure/channel_create.c
+++ b/src/core/ext/transport/chttp2/client/insecure/channel_create.c
@@ -41,6 +41,7 @@
 #include <grpc/support/slice_buffer.h>
 
 #include "src/core/ext/client_config/client_channel.h"
+#include "src/core/ext/client_config/http_connect_handshaker.h"
 #include "src/core/ext/client_config/resolver_registry.h"
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/channel_args.h"
@@ -88,14 +89,21 @@
 }
 
 static void on_handshake_done(grpc_exec_ctx *exec_ctx, grpc_endpoint *endpoint,
-                              grpc_channel_args *args, void *user_data,
+                              grpc_channel_args *args,
+                              gpr_slice_buffer *read_buffer, void *user_data,
                               grpc_error *error) {
   connector *c = user_data;
-  c->result->transport =
-      grpc_create_chttp2_transport(exec_ctx, args, endpoint, 1);
-  GPR_ASSERT(c->result->transport);
-  grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL, 0);
-  c->result->channel_args = args;
+  if (error != GRPC_ERROR_NONE) {
+    grpc_channel_args_destroy(args);
+    gpr_free(read_buffer);
+  } else {
+    c->result->transport =
+        grpc_create_chttp2_transport(exec_ctx, args, endpoint, 1);
+    GPR_ASSERT(c->result->transport);
+    grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport,
+                                        read_buffer);
+    c->result->channel_args = args;
+  }
   grpc_closure *notify = c->notify;
   c->notify = NULL;
   grpc_exec_ctx_sched(exec_ctx, notify, error, NULL);
@@ -153,7 +161,6 @@
   grpc_client_channel_factory base;
   gpr_refcount refs;
   grpc_channel_args *merge_args;
-  grpc_channel *master;
 } client_channel_factory;
 
 static void client_channel_factory_ref(
@@ -166,10 +173,6 @@
     grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory) {
   client_channel_factory *f = (client_channel_factory *)cc_factory;
   if (gpr_unref(&f->refs)) {
-    if (f->master != NULL) {
-      GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, f->master,
-                                  "client_channel_factory");
-    }
     grpc_channel_args_destroy(f->merge_args);
     gpr_free(f);
   }
@@ -187,6 +190,13 @@
   c->base.vtable = &connector_vtable;
   gpr_ref_init(&c->refs, 1);
   c->handshake_mgr = grpc_handshake_manager_create();
+  char *proxy_name = grpc_get_http_proxy_server();
+  if (proxy_name != NULL) {
+    grpc_handshake_manager_add(
+        c->handshake_mgr,
+        grpc_http_connect_handshaker_create(proxy_name, args->server_name));
+    gpr_free(proxy_name);
+  }
   args->args = final_args;
   s = grpc_subchannel_create(exec_ctx, &c->base, args);
   grpc_connector_unref(exec_ctx, &c->base);
@@ -203,15 +213,15 @@
   grpc_channel *channel = grpc_channel_create(exec_ctx, target, final_args,
                                               GRPC_CLIENT_CHANNEL, NULL);
   grpc_channel_args_destroy(final_args);
-  grpc_resolver *resolver = grpc_resolver_create(target, &f->base);
+  grpc_resolver *resolver = grpc_resolver_create(target);
   if (!resolver) {
     GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel,
                                 "client_channel_factory_create_channel");
     return NULL;
   }
 
-  grpc_client_channel_set_resolver(
-      exec_ctx, grpc_channel_get_channel_stack(channel), resolver);
+  grpc_client_channel_finish_initialization(
+      exec_ctx, grpc_channel_get_channel_stack(channel), resolver, &f->base);
   GRPC_RESOLVER_UNREF(exec_ctx, resolver, "create_channel");
 
   return channel;
@@ -243,12 +253,8 @@
 
   grpc_channel *channel = client_channel_factory_create_channel(
       &exec_ctx, &f->base, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, NULL);
-  if (channel != NULL) {
-    f->master = channel;
-    GRPC_CHANNEL_INTERNAL_REF(f->master, "grpc_insecure_channel_create");
-  }
-  grpc_client_channel_factory_unref(&exec_ctx, &f->base);
 
+  grpc_client_channel_factory_unref(&exec_ctx, &f->base);
   grpc_exec_ctx_finish(&exec_ctx);
 
   return channel != NULL ? channel : grpc_lame_client_channel_create(
diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
index ca435c2..b2c5e5b 100644
--- a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
+++ b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
@@ -75,7 +75,7 @@
   grpc_channel *channel = grpc_channel_create(
       &exec_ctx, target, final_args, GRPC_CLIENT_DIRECT_CHANNEL, transport);
   grpc_channel_args_destroy(final_args);
-  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
 
   grpc_exec_ctx_finish(&exec_ctx);
 
diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
index 4e33b6f..31c54ff 100644
--- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
+++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
@@ -41,6 +41,7 @@
 #include <grpc/support/slice_buffer.h>
 
 #include "src/core/ext/client_config/client_channel.h"
+#include "src/core/ext/client_config/http_connect_handshaker.h"
 #include "src/core/ext/client_config/resolver_registry.h"
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/channel_args.h"
@@ -114,8 +115,7 @@
     gpr_mu_unlock(&c->mu);
     c->result->transport = grpc_create_chttp2_transport(
         exec_ctx, c->args.channel_args, secure_endpoint, 1);
-    grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL,
-                                        0);
+    grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL);
     auth_context_arg = grpc_auth_context_to_arg(auth_context);
     c->result->channel_args =
         grpc_channel_args_copy_and_add(c->tmp_args, &auth_context_arg, 1);
@@ -126,10 +126,13 @@
 }
 
 static void on_handshake_done(grpc_exec_ctx *exec_ctx, grpc_endpoint *endpoint,
-                              grpc_channel_args *args, void *user_data,
+                              grpc_channel_args *args,
+                              gpr_slice_buffer *read_buffer, void *user_data,
                               grpc_error *error) {
   connector *c = user_data;
+  c->tmp_args = args;
   if (error != GRPC_ERROR_NONE) {
+    gpr_free(read_buffer);
     grpc_closure *notify = c->notify;
     c->notify = NULL;
     grpc_exec_ctx_sched(exec_ctx, notify, error, NULL);
@@ -137,10 +140,9 @@
     // TODO(roth, jboeuf): Convert security connector handshaking to use new
     // handshake API, and then move the code from on_secure_handshake_done()
     // into this function.
-    c->tmp_args = args;
     grpc_channel_security_connector_do_handshake(
-        exec_ctx, c->security_connector, endpoint, c->args.deadline,
-        on_secure_handshake_done, c);
+        exec_ctx, c->security_connector, endpoint, read_buffer,
+        c->args.deadline, on_secure_handshake_done, c);
   }
 }
 
@@ -219,7 +221,6 @@
   gpr_refcount refs;
   grpc_channel_args *merge_args;
   grpc_channel_security_connector *security_connector;
-  grpc_channel *master;
 } client_channel_factory;
 
 static void client_channel_factory_ref(
@@ -234,10 +235,6 @@
   if (gpr_unref(&f->refs)) {
     GRPC_SECURITY_CONNECTOR_UNREF(&f->security_connector->base,
                                   "client_channel_factory");
-    if (f->master != NULL) {
-      GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, f->master,
-                                  "client_channel_factory");
-    }
     grpc_channel_args_destroy(f->merge_args);
     gpr_free(f);
   }
@@ -255,6 +252,13 @@
   c->base.vtable = &connector_vtable;
   c->security_connector = f->security_connector;
   c->handshake_mgr = grpc_handshake_manager_create();
+  char *proxy_name = grpc_get_http_proxy_server();
+  if (proxy_name != NULL) {
+    grpc_handshake_manager_add(
+        c->handshake_mgr,
+        grpc_http_connect_handshaker_create(proxy_name, args->server_name));
+    gpr_free(proxy_name);
+  }
   gpr_mu_init(&c->mu);
   gpr_ref_init(&c->refs, 1);
   args->args = final_args;
@@ -275,10 +279,10 @@
                                               GRPC_CLIENT_CHANNEL, NULL);
   grpc_channel_args_destroy(final_args);
 
-  grpc_resolver *resolver = grpc_resolver_create(target, &f->base);
+  grpc_resolver *resolver = grpc_resolver_create(target);
   if (resolver != NULL) {
-    grpc_client_channel_set_resolver(
-        exec_ctx, grpc_channel_get_channel_stack(channel), resolver);
+    grpc_client_channel_finish_initialization(
+        exec_ctx, grpc_channel_get_channel_stack(channel), resolver, &f->base);
     GRPC_RESOLVER_UNREF(exec_ctx, resolver, "create");
   } else {
     GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel,
@@ -355,10 +359,6 @@
 
   grpc_channel *channel = client_channel_factory_create_channel(
       &exec_ctx, &f->base, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, NULL);
-  if (channel != NULL) {
-    f->master = channel;
-    GRPC_CHANNEL_INTERNAL_REF(f->master, "grpc_secure_channel_create");
-  }
 
   grpc_client_channel_factory_unref(&exec_ctx, &f->base);
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
index 9cd3747..f0e0742 100644
--- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
+++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
@@ -55,7 +55,8 @@
 } server_connect_state;
 
 static void on_handshake_done(grpc_exec_ctx *exec_ctx, grpc_endpoint *endpoint,
-                              grpc_channel_args *args, void *user_data,
+                              grpc_channel_args *args,
+                              gpr_slice_buffer *read_buffer, void *user_data,
                               grpc_error *error) {
   server_connect_state *state = user_data;
   if (error != GRPC_ERROR_NONE) {
@@ -64,6 +65,7 @@
     grpc_error_free_string(error_str);
     GRPC_ERROR_UNREF(error);
     grpc_handshake_manager_shutdown(exec_ctx, state->handshake_mgr);
+    gpr_free(read_buffer);
   } else {
     // Beware that the call to grpc_create_chttp2_transport() has to happen
     // before grpc_tcp_server_destroy(). This is fine here, but similar code
@@ -75,7 +77,7 @@
     grpc_server_setup_transport(exec_ctx, state->server, transport,
                                 state->accepting_pollset,
                                 grpc_server_get_channel_args(state->server));
-    grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0);
+    grpc_chttp2_transport_start_reading(exec_ctx, transport, read_buffer);
   }
   // Clean up.
   grpc_channel_args_destroy(args);
diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c
index 96bf4d6..9af17fb 100644
--- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c
+++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c
@@ -50,10 +50,10 @@
 #include "src/core/lib/surface/server.h"
 
 void grpc_server_add_insecure_channel_from_fd(grpc_server *server,
-                                              grpc_completion_queue *cq,
-                                              int fd) {
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+                                              void *reserved, int fd) {
+  GPR_ASSERT(reserved == NULL);
 
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   char *name;
   gpr_asprintf(&name, "fd:%d", fd);
 
@@ -65,17 +65,24 @@
   const grpc_channel_args *server_args = grpc_server_get_channel_args(server);
   grpc_transport *transport = grpc_create_chttp2_transport(
       &exec_ctx, server_args, server_endpoint, 0 /* is_client */);
-  grpc_endpoint_add_to_pollset(&exec_ctx, server_endpoint, grpc_cq_pollset(cq));
+
+  grpc_pollset **pollsets;
+  size_t num_pollsets = 0;
+  grpc_server_get_pollsets(server, &pollsets, &num_pollsets);
+
+  for (size_t i = 0; i < num_pollsets; i++) {
+    grpc_endpoint_add_to_pollset(&exec_ctx, server_endpoint, pollsets[i]);
+  }
+
   grpc_server_setup_transport(&exec_ctx, server, transport, NULL, server_args);
-  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
 #else  // !GPR_SUPPORT_CHANNELS_FROM_FD
 
 void grpc_server_add_insecure_channel_from_fd(grpc_server *server,
-                                              grpc_completion_queue *cq,
-                                              int fd) {
+                                              void *reserved, int fd) {
   GPR_ASSERT(0);
 }
 
diff --git a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
index ccea15a..da3e284 100644
--- a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
+++ b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
@@ -111,7 +111,7 @@
         grpc_server_setup_transport(exec_ctx, state->state->server, transport,
                                     state->accepting_pollset, args_copy);
         grpc_channel_args_destroy(args_copy);
-        grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0);
+        grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL);
       } else {
         /* We need to consume this here, because the server may already have
          * gone away. */
@@ -128,7 +128,8 @@
 }
 
 static void on_handshake_done(grpc_exec_ctx *exec_ctx, grpc_endpoint *endpoint,
-                              grpc_channel_args *args, void *user_data,
+                              grpc_channel_args *args,
+                              gpr_slice_buffer *read_buffer, void *user_data,
                               grpc_error *error) {
   server_secure_connect *state = user_data;
   if (error != GRPC_ERROR_NONE) {
@@ -136,9 +137,10 @@
     gpr_log(GPR_ERROR, "Handshaking failed: %s", error_str);
     grpc_error_free_string(error_str);
     GRPC_ERROR_UNREF(error);
+    grpc_channel_args_destroy(args);
+    gpr_free(read_buffer);
     grpc_handshake_manager_shutdown(exec_ctx, state->handshake_mgr);
     grpc_handshake_manager_destroy(exec_ctx, state->handshake_mgr);
-    grpc_channel_args_destroy(args);
     state_unref(state->state);
     gpr_free(state);
     return;
@@ -150,8 +152,8 @@
   // into this function.
   state->args = args;
   grpc_server_security_connector_do_handshake(
-      exec_ctx, state->state->sc, state->acceptor, endpoint, state->deadline,
-      on_secure_handshake_done, state);
+      exec_ctx, state->state->sc, state->acceptor, endpoint, read_buffer,
+      state->deadline, on_secure_handshake_done, state);
 }
 
 static void on_accept(grpc_exec_ctx *exec_ctx, void *statep, grpc_endpoint *tcp,
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index d050467..1dd7fef 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -33,6 +33,7 @@
 
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 
+#include <limits.h>
 #include <math.h>
 #include <stdio.h>
 #include <string.h>
@@ -46,12 +47,13 @@
 #include "src/core/ext/transport/chttp2/transport/http2_errors.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 #include "src/core/ext/transport/chttp2/transport/status_conversion.h"
-#include "src/core/ext/transport/chttp2/transport/timeout_encoding.h"
+#include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/http/parser.h"
 #include "src/core/lib/iomgr/workqueue.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/timeout_encoding.h"
 #include "src/core/lib/transport/transport_impl.h"
 
 #define DEFAULT_WINDOW 65535
@@ -89,8 +91,16 @@
 static void writing_action(grpc_exec_ctx *exec_ctx, void *t, grpc_error *error);
 static void reading_action(grpc_exec_ctx *exec_ctx, void *t, grpc_error *error);
 static void parsing_action(grpc_exec_ctx *exec_ctx, void *t, grpc_error *error);
-static void initiate_writing(grpc_exec_ctx *exec_ctx, void *t,
-                             grpc_error *error);
+static void reading_action_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                  grpc_error *error);
+static void post_parse_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                              grpc_error *error);
+static void initiate_writing_locked(grpc_exec_ctx *exec_ctx, void *t,
+                                    grpc_error *error);
+static void initiate_read_flush_locked(grpc_exec_ctx *exec_ctx, void *t,
+                                       grpc_error *error);
+static void terminate_writing_with_lock(grpc_exec_ctx *exec_ctx, void *t,
+                                        grpc_error *error);
 
 static void start_writing(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t);
 static void end_waiting_for_write(grpc_exec_ctx *exec_ctx,
@@ -104,11 +114,6 @@
 static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
                             grpc_error *error);
 
-/** Perform a transport_op */
-static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx,
-                                     grpc_chttp2_transport *t,
-                                     grpc_chttp2_stream *s, void *transport_op);
-
 /** Cancel a stream: coming from the transport API */
 static void cancel_from_api(grpc_exec_ctx *exec_ctx,
                             grpc_chttp2_transport_global *transport_global,
@@ -120,22 +125,10 @@
                            grpc_chttp2_stream_global *stream_global,
                            grpc_error *error);
 
-/** Add endpoint from this transport to pollset */
-static void add_to_pollset_locked(grpc_exec_ctx *exec_ctx,
-                                  grpc_chttp2_transport *t,
-                                  grpc_chttp2_stream *s_ignored, void *pollset);
-static void add_to_pollset_set_locked(grpc_exec_ctx *exec_ctx,
-                                      grpc_chttp2_transport *t,
-                                      grpc_chttp2_stream *s_ignored,
-                                      void *pollset_set);
-
 /** Start new streams that have been created if we can */
 static void maybe_start_some_streams(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global);
 
-static void finish_global_actions(grpc_exec_ctx *exec_ctx,
-                                  grpc_chttp2_transport *t);
-
 static void connectivity_state_set(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
     grpc_connectivity_state state, grpc_error *error, const char *reason);
@@ -148,14 +141,16 @@
     grpc_chttp2_stream_global *stream_global, size_t max_size_hint,
     size_t have_already);
 static void incoming_byte_stream_destroy_locked(grpc_exec_ctx *exec_ctx,
-                                                grpc_chttp2_transport *t,
-                                                grpc_chttp2_stream *s,
-                                                void *byte_stream);
+                                                void *byte_stream,
+                                                grpc_error *error_ignored);
 static void fail_pending_writes(grpc_exec_ctx *exec_ctx,
                                 grpc_chttp2_transport_global *transport_global,
                                 grpc_chttp2_stream_global *stream_global,
                                 grpc_error *error);
 
+static void set_write_state(grpc_chttp2_transport *t,
+                            grpc_chttp2_write_state state, const char *reason);
+
 /*******************************************************************************
  * CONSTRUCTION/DESTRUCTION/REFCOUNTING
  */
@@ -164,9 +159,7 @@
                                grpc_chttp2_transport *t) {
   size_t i;
 
-  gpr_mu_lock(&t->executor.mu);
-
-  GPR_ASSERT(t->ep == NULL);
+  grpc_endpoint_destroy(exec_ctx, t->ep);
 
   gpr_slice_buffer_destroy(&t->global.qbuf);
 
@@ -190,8 +183,7 @@
   grpc_chttp2_stream_map_destroy(&t->new_stream_map);
   grpc_connectivity_state_destroy(exec_ctx, &t->channel_callback.state_tracker);
 
-  gpr_mu_unlock(&t->executor.mu);
-  gpr_mu_destroy(&t->executor.mu);
+  grpc_combiner_destroy(exec_ctx, t->executor.combiner);
 
   /* callback remaining pings: they're not allowed to call into the transpot,
      and maybe they hold resources that need to be freed */
@@ -249,12 +241,13 @@
   memset(t, 0, sizeof(*t));
 
   t->base.vtable = &vtable;
+  t->executor.write_state = GRPC_CHTTP2_WRITES_CORKED;
   t->ep = ep;
-  /* one ref is for destroy, the other for when ep becomes NULL */
-  gpr_ref_init(&t->refs, 2);
+  /* one ref is for destroy */
+  gpr_ref_init(&t->refs, 1);
   /* ref is dropped at transport close() */
   gpr_ref_init(&t->shutdown_ep_refs, 1);
-  gpr_mu_init(&t->executor.mu);
+  t->executor.combiner = grpc_combiner_create(grpc_endpoint_get_workqueue(ep));
   t->peer_string = grpc_endpoint_get_peer(ep);
   t->endpoint_reading = 1;
   t->global.next_stream_id = is_client ? 1 : 2;
@@ -280,23 +273,22 @@
   grpc_chttp2_hpack_compressor_init(&t->writing.hpack_compressor);
   grpc_closure_init(&t->writing_action, writing_action, t);
   grpc_closure_init(&t->reading_action, reading_action, t);
+  grpc_closure_init(&t->reading_action_locked, reading_action_locked, t);
   grpc_closure_init(&t->parsing_action, parsing_action, t);
-  grpc_closure_init(&t->initiate_writing, initiate_writing, t);
+  grpc_closure_init(&t->post_parse_locked, post_parse_locked, t);
+  grpc_closure_init(&t->initiate_writing, initiate_writing_locked, t);
+  grpc_closure_init(&t->terminate_writing, terminate_writing_with_lock, t);
+  grpc_closure_init(&t->initiate_read_flush_locked, initiate_read_flush_locked,
+                    t);
+  grpc_closure_init(&t->writing.done_cb, grpc_chttp2_terminate_writing,
+                    &t->writing);
 
   gpr_slice_buffer_init(&t->parsing.qbuf);
   grpc_chttp2_goaway_parser_init(&t->parsing.goaway_parser);
   grpc_chttp2_hpack_parser_init(&t->parsing.hpack_parser);
 
-  grpc_closure_init(&t->writing.done_cb, grpc_chttp2_terminate_writing,
-                    &t->writing);
   gpr_slice_buffer_init(&t->read_buffer);
 
-  if (is_client) {
-    gpr_slice_buffer_add(
-        &t->global.qbuf,
-        gpr_slice_from_copied_string(GRPC_CHTTP2_CLIENT_CONNECT_STRING));
-    grpc_chttp2_initiate_write(exec_ctx, &t->global, false, "initial_write");
-  }
   /* 8 is a random stab in the dark as to a good initial size: it's small enough
      that it shouldn't waste memory for infrequently used connections, yet
      large enough that the exponential growth should happen nicely when it's
@@ -319,6 +311,13 @@
   t->global.force_send_settings = 1 << GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
   t->global.sent_local_settings = 0;
 
+  if (is_client) {
+    gpr_slice_buffer_add(
+        &t->writing.outbuf,
+        gpr_slice_from_copied_string(GRPC_CHTTP2_CLIENT_CONNECT_STRING));
+    grpc_chttp2_initiate_write(exec_ctx, &t->global, false, "initial_write");
+  }
+
   /* configure http2 the way we like it */
   if (is_client) {
     push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_ENABLE_PUSH, 0);
@@ -336,121 +335,115 @@
         if (is_client) {
           gpr_log(GPR_ERROR, "%s: is ignored on the client",
                   GRPC_ARG_MAX_CONCURRENT_STREAMS);
-        } else if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s: must be an integer",
-                  GRPC_ARG_MAX_CONCURRENT_STREAMS);
         } else {
-          push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
-                       (uint32_t)channel_args->args[i].value.integer);
+          const grpc_integer_options options = {-1, 0, INT_MAX};
+          const int value =
+              grpc_channel_arg_get_integer(&channel_args->args[i], options);
+          if (value >= 0) {
+            push_setting(exec_ctx, t,
+                         GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
+                         (uint32_t)value);
+          }
         }
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER)) {
-        if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s: must be an integer",
-                  GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER);
-        } else if ((t->global.next_stream_id & 1) !=
-                   (channel_args->args[i].value.integer & 1)) {
-          gpr_log(GPR_ERROR, "%s: low bit must be %d on %s",
-                  GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER,
-                  t->global.next_stream_id & 1,
-                  is_client ? "client" : "server");
-        } else {
-          t->global.next_stream_id =
-              (uint32_t)channel_args->args[i].value.integer;
+        const grpc_integer_options options = {-1, 0, INT_MAX};
+        const int value =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+        if (value >= 0) {
+          if ((t->global.next_stream_id & 1) != (value & 1)) {
+            gpr_log(GPR_ERROR, "%s: low bit must be %d on %s",
+                    GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER,
+                    t->global.next_stream_id & 1,
+                    is_client ? "client" : "server");
+          } else {
+            t->global.next_stream_id = (uint32_t)value;
+          }
         }
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES)) {
-        if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s: must be an integer",
-                  GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES);
-        } else if (channel_args->args[i].value.integer <= 5) {
-          gpr_log(GPR_ERROR, "%s: must be at least 5",
-                  GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES);
-        } else {
-          t->global.stream_lookahead =
-              (uint32_t)channel_args->args[i].value.integer;
+        const grpc_integer_options options = {-1, 5, INT_MAX};
+        const int value =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+        if (value >= 0) {
+          t->global.stream_lookahead = (uint32_t)value;
         }
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER)) {
-        if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s: must be an integer",
-                  GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER);
-        } else if (channel_args->args[i].value.integer < 0) {
-          gpr_log(GPR_ERROR, "%s: must be non-negative",
-                  GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER);
-        } else {
+        const grpc_integer_options options = {-1, 0, INT_MAX};
+        const int value =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+        if (value >= 0) {
           push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE,
-                       (uint32_t)channel_args->args[i].value.integer);
+                       (uint32_t)value);
         }
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER)) {
-        if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s: must be an integer",
-                  GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER);
-        } else if (channel_args->args[i].value.integer < 0) {
-          gpr_log(GPR_ERROR, "%s: must be non-negative",
-                  GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER);
-        } else {
+        const grpc_integer_options options = {-1, 0, INT_MAX};
+        const int value =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+        if (value >= 0) {
           grpc_chttp2_hpack_compressor_set_max_usable_size(
-              &t->writing.hpack_compressor,
-              (uint32_t)channel_args->args[i].value.integer);
+              &t->writing.hpack_compressor, (uint32_t)value);
         }
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_MAX_METADATA_SIZE)) {
+        const grpc_integer_options options = {-1, 0, INT_MAX};
+        const int value =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+        if (value >= 0) {
+          push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
+                       (uint32_t)value);
+        }
+      } else if (0 == strcmp(channel_args->args[i].key,
+                             GRPC_ARG_HTTP2_MAX_FRAME_SIZE)) {
         if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
           gpr_log(GPR_ERROR, "%s: must be an integer",
-                  GRPC_ARG_MAX_METADATA_SIZE);
-        } else if (channel_args->args[i].value.integer < 0) {
-          gpr_log(GPR_ERROR, "%s: must be non-negative",
-                  GRPC_ARG_MAX_METADATA_SIZE);
+                  GRPC_ARG_HTTP2_MAX_FRAME_SIZE);
+        } else if (channel_args->args[i].value.integer < 16384 ||
+                   channel_args->args[i].value.integer > 16777215) {
+          gpr_log(GPR_ERROR, "%s: must be between 16384 and 16777215",
+                  GRPC_ARG_HTTP2_MAX_FRAME_SIZE);
         } else {
-          push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
+          push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE,
                        (uint32_t)channel_args->args[i].value.integer);
         }
       }
     }
   }
+
+  set_write_state(t, GRPC_CHTTP2_WRITING_INACTIVE, "uncork");
+  grpc_chttp2_initiate_write(exec_ctx, &t->global, false, "init");
 }
 
-static void destroy_transport_locked(grpc_exec_ctx *exec_ctx,
-                                     grpc_chttp2_transport *t,
-                                     grpc_chttp2_stream *s_ignored,
-                                     void *arg_ignored) {
+static void destroy_transport_locked(grpc_exec_ctx *exec_ctx, void *tp,
+                                     grpc_error *error) {
+  grpc_chttp2_transport *t = tp;
   t->destroying = 1;
   drop_connection(exec_ctx, t, GRPC_ERROR_CREATE("Transport destroyed"));
+  UNREF_TRANSPORT(exec_ctx, t, "destroy");
 }
 
 static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) {
   grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt;
-  grpc_chttp2_run_with_global_lock(exec_ctx, t, NULL, destroy_transport_locked,
-                                   NULL, 0);
-  UNREF_TRANSPORT(exec_ctx, t, "destroy");
+  grpc_combiner_execute(exec_ctx, t->executor.combiner,
+                        grpc_closure_create(destroy_transport_locked, t),
+                        GRPC_ERROR_NONE);
 }
 
 /** block grpc_endpoint_shutdown being called until a paired
     allow_endpoint_shutdown is made */
 static void prevent_endpoint_shutdown(grpc_chttp2_transport *t) {
-  GPR_ASSERT(t->ep);
   gpr_ref(&t->shutdown_ep_refs);
 }
 
 static void allow_endpoint_shutdown_locked(grpc_exec_ctx *exec_ctx,
                                            grpc_chttp2_transport *t) {
   if (gpr_unref(&t->shutdown_ep_refs)) {
-    if (t->ep) {
-      grpc_endpoint_shutdown(exec_ctx, t->ep);
-    }
+    grpc_endpoint_shutdown(exec_ctx, t->ep);
   }
 }
 
-static void destroy_endpoint(grpc_exec_ctx *exec_ctx,
-                             grpc_chttp2_transport *t) {
-  grpc_endpoint_destroy(exec_ctx, t->ep);
-  t->ep = NULL;
-  /* safe because we'll still have the ref for write */
-  UNREF_TRANSPORT(exec_ctx, t, "disconnect");
-}
-
 static void close_transport_locked(grpc_exec_ctx *exec_ctx,
                                    grpc_chttp2_transport *t,
                                    grpc_error *error) {
@@ -461,9 +454,7 @@
     t->closed = 1;
     connectivity_state_set(exec_ctx, &t->global, GRPC_CHANNEL_SHUTDOWN,
                            GRPC_ERROR_REF(error), "close_transport");
-    if (t->ep) {
-      allow_endpoint_shutdown_locked(exec_ctx, t);
-    }
+    allow_endpoint_shutdown_locked(exec_ctx, t);
 
     /* flush writable stream list to avoid dangling references */
     grpc_chttp2_stream_global *stream_global;
@@ -497,21 +488,23 @@
 }
 #endif
 
-static void finish_init_stream_locked(grpc_exec_ctx *exec_ctx,
-                                      grpc_chttp2_transport *t,
-                                      grpc_chttp2_stream *s,
-                                      void *arg_ignored) {
-  grpc_chttp2_register_stream(t, s);
+static void finish_init_stream_locked(grpc_exec_ctx *exec_ctx, void *sp,
+                                      grpc_error *error) {
+  grpc_chttp2_stream *s = sp;
+  grpc_chttp2_register_stream(s->t, s);
+  GRPC_CHTTP2_STREAM_UNREF(exec_ctx, &s->global, "init");
 }
 
 static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
                        grpc_stream *gs, grpc_stream_refcount *refcount,
                        const void *server_data) {
+  GPR_TIMER_BEGIN("init_stream", 0);
   grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt;
   grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs;
 
   memset(s, 0, sizeof(*s));
 
+  s->t = t;
   s->refcount = refcount;
   /* We reserve one 'active stream' that's dropped when the stream is
      read-closed. The others are for incoming_byte_streams that are actively
@@ -546,16 +539,21 @@
     s->global.in_stream_map = true;
   }
 
-  grpc_chttp2_run_with_global_lock(exec_ctx, t, s, finish_init_stream_locked,
-                                   NULL, 0);
+  grpc_closure_init(&s->init_stream, finish_init_stream_locked, s);
+  GRPC_CHTTP2_STREAM_REF(&s->global, "init");
+  grpc_combiner_execute(exec_ctx, t->executor.combiner, &s->init_stream,
+                        GRPC_ERROR_NONE);
+
+  GPR_TIMER_END("init_stream", 0);
 
   return 0;
 }
 
-static void destroy_stream_locked(grpc_exec_ctx *exec_ctx,
-                                  grpc_chttp2_transport *t,
-                                  grpc_chttp2_stream *s, void *arg) {
+static void destroy_stream_locked(grpc_exec_ctx *exec_ctx, void *sp,
+                                  grpc_error *error) {
   grpc_byte_stream *bs;
+  grpc_chttp2_stream *s = sp;
+  grpc_chttp2_transport *t = s->t;
 
   GPR_TIMER_BEGIN("destroy_stream", 0);
 
@@ -574,7 +572,7 @@
 
   while (
       (bs = grpc_chttp2_incoming_frame_queue_pop(&s->global.incoming_frames))) {
-    incoming_byte_stream_destroy_locked(exec_ctx, NULL, NULL, bs);
+    incoming_byte_stream_destroy_locked(exec_ctx, bs, GRPC_ERROR_NONE);
   }
 
   grpc_chttp2_list_remove_unannounced_incoming_window_available(&t->global,
@@ -611,16 +609,20 @@
 
   GPR_TIMER_END("destroy_stream", 0);
 
-  gpr_free(arg);
+  gpr_free(s->destroy_stream_arg);
 }
 
 static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
                            grpc_stream *gs, void *and_free_memory) {
+  GPR_TIMER_BEGIN("destroy_stream", 0);
   grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt;
   grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs;
 
-  grpc_chttp2_run_with_global_lock(exec_ctx, t, s, destroy_stream_locked,
-                                   and_free_memory, 0);
+  s->destroy_stream_arg = and_free_memory;
+  grpc_closure_init(&s->destroy_stream, destroy_stream_locked, s);
+  grpc_combiner_execute(exec_ctx, t->executor.combiner, &s->destroy_stream,
+                        GRPC_ERROR_NONE);
+  GPR_TIMER_END("destroy_stream", 0);
 }
 
 grpc_chttp2_stream_parsing *grpc_chttp2_parsing_lookup_stream(
@@ -651,12 +653,10 @@
 
 static const char *write_state_name(grpc_chttp2_write_state state) {
   switch (state) {
+    case GRPC_CHTTP2_WRITES_CORKED:
+      return "CORKED";
     case GRPC_CHTTP2_WRITING_INACTIVE:
       return "INACTIVE";
-    case GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER:
-      return "REQUESTED[p=0]";
-    case GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER:
-      return "REQUESTED[p=1]";
     case GRPC_CHTTP2_WRITE_SCHEDULED:
       return "SCHEDULED";
     case GRPC_CHTTP2_WRITING:
@@ -679,120 +679,18 @@
   t->executor.write_state = state;
 }
 
-static void finish_global_actions(grpc_exec_ctx *exec_ctx,
-                                  grpc_chttp2_transport *t) {
-  grpc_chttp2_executor_action_header *hdr;
-  grpc_chttp2_executor_action_header *next;
-
-  GPR_TIMER_BEGIN("finish_global_actions", 0);
-
-  for (;;) {
-    check_read_ops(exec_ctx, &t->global);
-
-    gpr_mu_lock(&t->executor.mu);
-    if (t->executor.pending_actions_head != NULL) {
-      hdr = t->executor.pending_actions_head;
-      t->executor.pending_actions_head = t->executor.pending_actions_tail =
-          NULL;
-      gpr_mu_unlock(&t->executor.mu);
-      while (hdr != NULL) {
-        GPR_TIMER_BEGIN("chttp2:locked_action", 0);
-        hdr->action(exec_ctx, t, hdr->stream, hdr->arg);
-        GPR_TIMER_END("chttp2:locked_action", 0);
-        next = hdr->next;
-        gpr_free(hdr);
-        UNREF_TRANSPORT(exec_ctx, t, "pending_action");
-        hdr = next;
-      }
-      continue;
-    } else {
-      t->executor.global_active = false;
-      switch (t->executor.write_state) {
-        case GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER:
-          set_write_state(t, GRPC_CHTTP2_WRITE_SCHEDULED, "unlocking");
-          REF_TRANSPORT(t, "initiate_writing");
-          gpr_mu_unlock(&t->executor.mu);
-          grpc_exec_ctx_sched(
-              exec_ctx, &t->initiate_writing, GRPC_ERROR_NONE,
-              t->ep != NULL ? grpc_endpoint_get_workqueue(t->ep) : NULL);
-          break;
-        case GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER:
-          start_writing(exec_ctx, t);
-          gpr_mu_unlock(&t->executor.mu);
-          break;
-        case GRPC_CHTTP2_WRITING_INACTIVE:
-        case GRPC_CHTTP2_WRITING:
-        case GRPC_CHTTP2_WRITING_STALE_WITH_POLLER:
-        case GRPC_CHTTP2_WRITING_STALE_NO_POLLER:
-        case GRPC_CHTTP2_WRITE_SCHEDULED:
-          gpr_mu_unlock(&t->executor.mu);
-          break;
-      }
-    }
-    break;
-  }
-
-  GPR_TIMER_END("finish_global_actions", 0);
+static void initiate_writing_locked(grpc_exec_ctx *exec_ctx, void *tp,
+                                    grpc_error *error) {
+  grpc_chttp2_transport *t = tp;
+  GPR_ASSERT(t->executor.write_state == GRPC_CHTTP2_WRITE_SCHEDULED);
+  start_writing(exec_ctx, t);
 }
 
-void grpc_chttp2_run_with_global_lock(grpc_exec_ctx *exec_ctx,
-                                      grpc_chttp2_transport *t,
-                                      grpc_chttp2_stream *optional_stream,
-                                      grpc_chttp2_locked_action action,
-                                      void *arg, size_t sizeof_arg) {
-  grpc_chttp2_executor_action_header *hdr;
-
-  GPR_TIMER_BEGIN("grpc_chttp2_run_with_global_lock", 0);
-
-  REF_TRANSPORT(t, "run_global");
-  gpr_mu_lock(&t->executor.mu);
-
-  for (;;) {
-    if (!t->executor.global_active) {
-      t->executor.global_active = 1;
-      gpr_mu_unlock(&t->executor.mu);
-
-      GPR_TIMER_BEGIN("chttp2:locked_action", 0);
-      action(exec_ctx, t, optional_stream, arg);
-      GPR_TIMER_END("chttp2:locked_action", 0);
-
-      finish_global_actions(exec_ctx, t);
-    } else {
-      gpr_mu_unlock(&t->executor.mu);
-
-      hdr = gpr_malloc(sizeof(*hdr) + sizeof_arg);
-      hdr->stream = optional_stream;
-      hdr->action = action;
-      if (sizeof_arg == 0) {
-        hdr->arg = arg;
-      } else {
-        hdr->arg = hdr + 1;
-        memcpy(hdr->arg, arg, sizeof_arg);
-      }
-
-      gpr_mu_lock(&t->executor.mu);
-      if (!t->executor.global_active) {
-        /* global lock was released while allocating memory: release & retry */
-        gpr_free(hdr);
-        continue;
-      }
-      hdr->next = NULL;
-      if (t->executor.pending_actions_head != NULL) {
-        t->executor.pending_actions_tail =
-            t->executor.pending_actions_tail->next = hdr;
-      } else {
-        t->executor.pending_actions_tail = t->executor.pending_actions_head =
-            hdr;
-      }
-      REF_TRANSPORT(t, "pending_action");
-      gpr_mu_unlock(&t->executor.mu);
-    }
-    break;
-  }
-
-  UNREF_TRANSPORT(exec_ctx, t, "run_global");
-
-  GPR_TIMER_END("grpc_chttp2_run_with_global_lock", 0);
+static void initiate_read_flush_locked(grpc_exec_ctx *exec_ctx, void *tp,
+                                       grpc_error *error) {
+  grpc_chttp2_transport *t = tp;
+  t->executor.check_read_ops_scheduled = false;
+  check_read_ops(exec_ctx, &t->global);
 }
 
 /*******************************************************************************
@@ -802,11 +700,12 @@
 void grpc_chttp2_initiate_write(grpc_exec_ctx *exec_ctx,
                                 grpc_chttp2_transport_global *transport_global,
                                 bool covered_by_poller, const char *reason) {
+  GPR_TIMER_BEGIN("grpc_chttp2_initiate_write", 0);
+
   /* Perform state checks, and transition to a scheduled state if appropriate.
-     Each time we finish the global lock execution, we check if we need to
-     write. If we do:
-      - (if there is a poller surrounding the write) schedule
-        initiate_writing, which locks and calls initiate_writing_locked to...
+     If we are inactive, schedule a write chain to begin once the transport
+     combiner finishes any executions in its current batch (which may be
+     scheduled AFTER this code executes). The write chain will:
       - call start_writing, which verifies (under the global lock) that there
         are things that need to be written by calling
         grpc_chttp2_unlocking_check_writes, and if so schedules writing_action
@@ -816,30 +715,27 @@
         to do *another* write immediately, and if so loops back to
         start_writing.
 
-      Current problems:
+     Current problems:
        - too much lock entry/exiting
        - the writing thread can become stuck indefinitely (punt through the
          workqueue periodically to fix) */
 
   grpc_chttp2_transport *t = TRANSPORT_FROM_GLOBAL(transport_global);
   switch (t->executor.write_state) {
+    case GRPC_CHTTP2_WRITES_CORKED:
+      break;
     case GRPC_CHTTP2_WRITING_INACTIVE:
-      set_write_state(t, covered_by_poller
-                             ? GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER
-                             : GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER,
-                      reason);
-      break;
-    case GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER:
-      /* nothing to do: write already requested */
-      break;
-    case GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER:
-      if (covered_by_poller) {
-        /* upgrade to note poller is available to cover the write */
-        set_write_state(t, GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER, reason);
-      }
+      set_write_state(t, GRPC_CHTTP2_WRITE_SCHEDULED, reason);
+      REF_TRANSPORT(t, "writing");
+      grpc_combiner_execute_finally(exec_ctx, t->executor.combiner,
+                                    &t->initiate_writing, GRPC_ERROR_NONE,
+                                    covered_by_poller);
       break;
     case GRPC_CHTTP2_WRITE_SCHEDULED:
-      /* nothing to do: write already scheduled */
+      if (covered_by_poller) {
+        /* upgrade to note poller is available to cover the write */
+        grpc_combiner_force_async_finally(t->executor.combiner);
+      }
       break;
     case GRPC_CHTTP2_WRITING:
       set_write_state(t,
@@ -857,15 +753,15 @@
       }
       break;
   }
+  GPR_TIMER_END("grpc_chttp2_initiate_write", 0);
 }
 
 static void start_writing(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) {
-  GPR_ASSERT(t->executor.write_state == GRPC_CHTTP2_WRITE_SCHEDULED ||
-             t->executor.write_state == GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER);
+  GPR_TIMER_BEGIN("start_writing", 0);
+  GPR_ASSERT(t->executor.write_state == GRPC_CHTTP2_WRITE_SCHEDULED);
   if (!t->closed &&
       grpc_chttp2_unlocking_check_writes(exec_ctx, &t->global, &t->writing)) {
     set_write_state(t, GRPC_CHTTP2_WRITING, "start_writing");
-    REF_TRANSPORT(t, "writing");
     prevent_endpoint_shutdown(t);
     grpc_exec_ctx_sched(exec_ctx, &t->writing_action, GRPC_ERROR_NONE, NULL);
   } else {
@@ -876,25 +772,10 @@
       set_write_state(t, GRPC_CHTTP2_WRITING_INACTIVE,
                       "start_writing:nothing_to_write");
     }
-    end_waiting_for_write(exec_ctx, t, GRPC_ERROR_CREATE("Nothing to write"));
-    if (t->ep && !t->endpoint_reading) {
-      destroy_endpoint(exec_ctx, t);
-    }
+    end_waiting_for_write(exec_ctx, t, GRPC_ERROR_NONE);
+    UNREF_TRANSPORT(exec_ctx, t, "writing");
   }
-}
-
-static void initiate_writing_locked(grpc_exec_ctx *exec_ctx,
-                                    grpc_chttp2_transport *t,
-                                    grpc_chttp2_stream *s_unused,
-                                    void *arg_ignored) {
-  start_writing(exec_ctx, t);
-  UNREF_TRANSPORT(exec_ctx, t, "initiate_writing");
-}
-
-static void initiate_writing(grpc_exec_ctx *exec_ctx, void *arg,
-                             grpc_error *error) {
-  grpc_chttp2_run_with_global_lock(exec_ctx, arg, NULL, initiate_writing_locked,
-                                   NULL, 0);
+  GPR_TIMER_END("start_writing", 0);
 }
 
 void grpc_chttp2_become_writable(grpc_exec_ctx *exec_ctx,
@@ -925,6 +806,8 @@
   }
 }
 
+/* error may be GRPC_ERROR_NONE if there is no error allocated yet.
+   In that case, use "reason" as the text for a new error. */
 static void end_waiting_for_write(grpc_exec_ctx *exec_ctx,
                                   grpc_chttp2_transport *t, grpc_error *error) {
   grpc_chttp2_stream_global *stream_global;
@@ -937,12 +820,10 @@
   GRPC_ERROR_UNREF(error);
 }
 
-static void terminate_writing_with_lock(grpc_exec_ctx *exec_ctx,
-                                        grpc_chttp2_transport *t,
-                                        grpc_chttp2_stream *s_ignored,
-                                        void *a) {
-  grpc_error *error = a;
-
+static void terminate_writing_with_lock(grpc_exec_ctx *exec_ctx, void *tp,
+                                        grpc_error *error) {
+  GPR_TIMER_BEGIN("terminate_writing_with_lock", 0);
+  grpc_chttp2_transport *t = tp;
   allow_endpoint_shutdown_locked(exec_ctx, t);
 
   if (error != GRPC_ERROR_NONE) {
@@ -951,39 +832,46 @@
 
   grpc_chttp2_cleanup_writing(exec_ctx, &t->global, &t->writing);
 
-  end_waiting_for_write(exec_ctx, t, error);
+  end_waiting_for_write(exec_ctx, t, GRPC_ERROR_REF(error));
 
   switch (t->executor.write_state) {
+    case GRPC_CHTTP2_WRITES_CORKED:
     case GRPC_CHTTP2_WRITING_INACTIVE:
-    case GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER:
-    case GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER:
     case GRPC_CHTTP2_WRITE_SCHEDULED:
       GPR_UNREACHABLE_CODE(break);
     case GRPC_CHTTP2_WRITING:
+      GPR_TIMER_MARK("state=writing", 0);
       set_write_state(t, GRPC_CHTTP2_WRITING_INACTIVE, "terminate_writing");
       break;
     case GRPC_CHTTP2_WRITING_STALE_WITH_POLLER:
-      set_write_state(t, GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER,
-                      "terminate_writing");
+      GPR_TIMER_MARK("state=writing_stale_with_poller", 0);
+      set_write_state(t, GRPC_CHTTP2_WRITE_SCHEDULED, "terminate_writing");
+      REF_TRANSPORT(t, "writing");
+      grpc_combiner_execute_finally(exec_ctx, t->executor.combiner,
+                                    &t->initiate_writing, GRPC_ERROR_NONE,
+                                    true);
       break;
     case GRPC_CHTTP2_WRITING_STALE_NO_POLLER:
-      set_write_state(t, GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER,
-                      "terminate_writing");
+      GPR_TIMER_MARK("state=writing_stale_no_poller", 0);
+      set_write_state(t, GRPC_CHTTP2_WRITE_SCHEDULED, "terminate_writing");
+      REF_TRANSPORT(t, "writing");
+      grpc_combiner_execute_finally(exec_ctx, t->executor.combiner,
+                                    &t->initiate_writing, GRPC_ERROR_NONE,
+                                    false);
       break;
   }
 
-  if (t->ep && !t->endpoint_reading) {
-    destroy_endpoint(exec_ctx, t);
-  }
-
   UNREF_TRANSPORT(exec_ctx, t, "writing");
+  GPR_TIMER_END("terminate_writing_with_lock", 0);
 }
 
 void grpc_chttp2_terminate_writing(grpc_exec_ctx *exec_ctx,
                                    void *transport_writing, grpc_error *error) {
+  GPR_TIMER_BEGIN("grpc_chttp2_terminate_writing", 0);
   grpc_chttp2_transport *t = TRANSPORT_FROM_WRITING(transport_writing);
-  grpc_chttp2_run_with_global_lock(
-      exec_ctx, t, NULL, terminate_writing_with_lock, GRPC_ERROR_REF(error), 0);
+  grpc_combiner_execute(exec_ctx, t->executor.combiner, &t->terminate_writing,
+                        GRPC_ERROR_REF(error));
+  GPR_TIMER_END("grpc_chttp2_terminate_writing", 0);
 }
 
 static void writing_action(grpc_exec_ctx *exec_ctx, void *gt,
@@ -1127,15 +1015,22 @@
 
 static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {}
 
-static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx,
-                                     grpc_chttp2_transport *t,
-                                     grpc_chttp2_stream *s, void *stream_op) {
+static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
+                                     grpc_error *error_ignored) {
   GPR_TIMER_BEGIN("perform_stream_op_locked", 0);
 
   grpc_transport_stream_op *op = stream_op;
+  grpc_chttp2_transport *t = op->transport_private.args[0];
+  grpc_chttp2_stream *s = op->transport_private.args[1];
   grpc_chttp2_transport_global *transport_global = &t->global;
   grpc_chttp2_stream_global *stream_global = &s->global;
 
+  if (grpc_http_trace) {
+    char *str = grpc_transport_stream_op_string(op);
+    gpr_log(GPR_DEBUG, "perform_stream_op_locked: %s", str);
+    gpr_free(str);
+  }
+
   grpc_closure *on_complete = op->on_complete;
   if (on_complete == NULL) {
     on_complete = grpc_closure_create(do_nothing, NULL);
@@ -1190,7 +1085,8 @@
     } else {
       if (contains_non_ok_status(transport_global, op->send_initial_metadata)) {
         stream_global->seen_error = true;
-        grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+        grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                            stream_global);
       }
       if (!stream_global->write_closed) {
         if (transport_global->is_client) {
@@ -1257,7 +1153,8 @@
       if (contains_non_ok_status(transport_global,
                                  op->send_trailing_metadata)) {
         stream_global->seen_error = true;
-        grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+        grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                            stream_global);
       }
       if (stream_global->write_closed) {
         stream_global->send_trailing_metadata = NULL;
@@ -1282,7 +1179,8 @@
     stream_global->recv_initial_metadata_ready =
         op->recv_initial_metadata_ready;
     stream_global->recv_initial_metadata = op->recv_initial_metadata;
-    grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+    grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                        stream_global);
   }
 
   if (op->recv_message != NULL) {
@@ -1296,7 +1194,8 @@
           exec_ctx, transport_global, stream_global,
           transport_global->stream_lookahead, 0);
     }
-    grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+    grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                        stream_global);
   }
 
   if (op->recv_trailing_metadata != NULL) {
@@ -1305,21 +1204,30 @@
         add_closure_barrier(on_complete);
     stream_global->recv_trailing_metadata = op->recv_trailing_metadata;
     stream_global->final_metadata_requested = true;
-    grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+    grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                        stream_global);
   }
 
   grpc_chttp2_complete_closure_step(exec_ctx, transport_global, stream_global,
                                     &on_complete, GRPC_ERROR_NONE);
 
   GPR_TIMER_END("perform_stream_op_locked", 0);
+  GRPC_CHTTP2_STREAM_UNREF(exec_ctx, &s->global, "perform_stream_op");
 }
 
 static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
                               grpc_stream *gs, grpc_transport_stream_op *op) {
+  GPR_TIMER_BEGIN("perform_stream_op", 0);
   grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt;
   grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs;
-  grpc_chttp2_run_with_global_lock(exec_ctx, t, s, perform_stream_op_locked, op,
-                                   sizeof(*op));
+  grpc_closure_init(&op->transport_private.closure, perform_stream_op_locked,
+                    op);
+  op->transport_private.args[0] = gt;
+  op->transport_private.args[1] = gs;
+  GRPC_CHTTP2_STREAM_REF(&s->global, "perform_stream_op");
+  grpc_combiner_execute(exec_ctx, t->executor.combiner,
+                        &op->transport_private.closure, GRPC_ERROR_NONE);
+  GPR_TIMER_END("perform_stream_op", 0);
 }
 
 static void send_ping_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
@@ -1341,13 +1249,20 @@
   grpc_chttp2_initiate_write(exec_ctx, &t->global, true, "send_ping");
 }
 
-static void ack_ping_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
-                            grpc_chttp2_stream *s, void *opaque_8bytes) {
+typedef struct ack_ping_args {
+  grpc_closure closure;
+  grpc_chttp2_transport *t;
+  uint8_t opaque_8bytes[8];
+} ack_ping_args;
+
+static void ack_ping_locked(grpc_exec_ctx *exec_ctx, void *a,
+                            grpc_error *error_ignored) {
+  ack_ping_args *args = a;
   grpc_chttp2_outstanding_ping *ping;
-  grpc_chttp2_transport_global *transport_global = &t->global;
+  grpc_chttp2_transport_global *transport_global = &args->t->global;
   for (ping = transport_global->pings.next; ping != &transport_global->pings;
        ping = ping->next) {
-    if (0 == memcmp(opaque_8bytes, ping->id, 8)) {
+    if (0 == memcmp(args->opaque_8bytes, ping->id, 8)) {
       grpc_exec_ctx_sched(exec_ctx, ping->on_recv, GRPC_ERROR_NONE, NULL);
       ping->next->prev = ping->prev;
       ping->prev->next = ping->next;
@@ -1355,21 +1270,27 @@
       break;
     }
   }
+  UNREF_TRANSPORT(exec_ctx, args->t, "ack_ping");
+  gpr_free(args);
 }
 
 void grpc_chttp2_ack_ping(grpc_exec_ctx *exec_ctx,
                           grpc_chttp2_transport_parsing *transport_parsing,
                           const uint8_t *opaque_8bytes) {
-  grpc_chttp2_run_with_global_lock(
-      exec_ctx, TRANSPORT_FROM_PARSING(transport_parsing), NULL,
-      ack_ping_locked, (void *)opaque_8bytes, 8);
+  ack_ping_args *args = gpr_malloc(sizeof(*args));
+  args->t = TRANSPORT_FROM_PARSING(transport_parsing);
+  memcpy(args->opaque_8bytes, opaque_8bytes, sizeof(args->opaque_8bytes));
+  grpc_closure_init(&args->closure, ack_ping_locked, args);
+  REF_TRANSPORT(args->t, "ack_ping");
+  grpc_combiner_execute(exec_ctx, args->t->executor.combiner, &args->closure,
+                        GRPC_ERROR_NONE);
 }
 
 static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx,
-                                        grpc_chttp2_transport *t,
-                                        grpc_chttp2_stream *s_unused,
-                                        void *stream_op) {
+                                        void *stream_op,
+                                        grpc_error *error_ignored) {
   grpc_transport_op *op = stream_op;
+  grpc_chttp2_transport *t = op->transport_private.args[0];
   grpc_error *close_transport = op->disconnect_with_error;
 
   /* If there's a set_accept_stream ensure that we're not parsing
@@ -1381,8 +1302,6 @@
     return;
   }
 
-  grpc_exec_ctx_sched(exec_ctx, op->on_consumed, GRPC_ERROR_NONE, NULL);
-
   if (op->on_connectivity_state_change != NULL) {
     grpc_connectivity_state_notify_on_state_change(
         exec_ctx, &t->channel_callback.state_tracker, op->connectivity_state,
@@ -1408,11 +1327,11 @@
   }
 
   if (op->bind_pollset) {
-    add_to_pollset_locked(exec_ctx, t, NULL, op->bind_pollset);
+    grpc_endpoint_add_to_pollset(exec_ctx, t->ep, op->bind_pollset);
   }
 
   if (op->bind_pollset_set) {
-    add_to_pollset_set_locked(exec_ctx, t, NULL, op->bind_pollset_set);
+    grpc_endpoint_add_to_pollset_set(exec_ctx, t->ep, op->bind_pollset_set);
   }
 
   if (op->send_ping) {
@@ -1422,13 +1341,21 @@
   if (close_transport != GRPC_ERROR_NONE) {
     close_transport_locked(exec_ctx, t, close_transport);
   }
+
+  grpc_exec_ctx_sched(exec_ctx, op->on_consumed, GRPC_ERROR_NONE, NULL);
+
+  UNREF_TRANSPORT(exec_ctx, t, "transport_op");
 }
 
 static void perform_transport_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
                                  grpc_transport_op *op) {
   grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt;
-  grpc_chttp2_run_with_global_lock(
-      exec_ctx, t, NULL, perform_transport_op_locked, op, sizeof(*op));
+  op->transport_private.args[0] = gt;
+  grpc_closure_init(&op->transport_private.closure, perform_transport_op_locked,
+                    op);
+  REF_TRANSPORT(t, "transport_op");
+  grpc_combiner_execute(exec_ctx, t->executor.combiner,
+                        &op->transport_private.closure, GRPC_ERROR_NONE);
 }
 
 /*******************************************************************************
@@ -1437,6 +1364,7 @@
 
 static void check_read_ops(grpc_exec_ctx *exec_ctx,
                            grpc_chttp2_transport_global *transport_global) {
+  GPR_TIMER_BEGIN("check_read_ops", 0);
   grpc_chttp2_stream_global *stream_global;
   grpc_byte_stream *bs;
   while (
@@ -1446,7 +1374,7 @@
       if (stream_global->seen_error) {
         while ((bs = grpc_chttp2_incoming_frame_queue_pop(
                     &stream_global->incoming_frames)) != NULL) {
-          incoming_byte_stream_destroy_locked(exec_ctx, NULL, NULL, bs);
+          incoming_byte_stream_destroy_locked(exec_ctx, bs, GRPC_ERROR_NONE);
         }
         if (stream_global->exceeded_metadata_size) {
           cancel_from_api(
@@ -1469,7 +1397,7 @@
              stream_global->seen_error &&
              (bs = grpc_chttp2_incoming_frame_queue_pop(
                   &stream_global->incoming_frames)) != NULL) {
-        incoming_byte_stream_destroy_locked(exec_ctx, NULL, NULL, bs);
+        incoming_byte_stream_destroy_locked(exec_ctx, bs, GRPC_ERROR_NONE);
       }
       if (stream_global->incoming_frames.head != NULL) {
         *stream_global->recv_message = grpc_chttp2_incoming_frame_queue_pop(
@@ -1490,7 +1418,7 @@
       if (stream_global->seen_error) {
         while ((bs = grpc_chttp2_incoming_frame_queue_pop(
                     &stream_global->incoming_frames)) != NULL) {
-          incoming_byte_stream_destroy_locked(exec_ctx, NULL, NULL, bs);
+          incoming_byte_stream_destroy_locked(exec_ctx, bs, GRPC_ERROR_NONE);
         }
         if (stream_global->exceeded_metadata_size) {
           cancel_from_api(
@@ -1511,6 +1439,7 @@
       }
     }
   }
+  GPR_TIMER_END("check_read_ops", 0);
 }
 
 static void decrement_active_streams_locked(
@@ -1518,7 +1447,8 @@
     grpc_chttp2_stream_global *stream_global) {
   if ((stream_global->all_incoming_byte_streams_finished =
            gpr_unref(&stream_global->active_streams))) {
-    grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+    grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                        stream_global);
   }
 }
 
@@ -1622,7 +1552,8 @@
   }
   if (due_to_error != GRPC_ERROR_NONE && !stream_global->seen_error) {
     stream_global->seen_error = true;
-    grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+    grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                        stream_global);
   }
   grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, 1,
                                  1, due_to_error);
@@ -1634,7 +1565,8 @@
                              grpc_status_code status, gpr_slice *slice) {
   if (status != GRPC_STATUS_OK) {
     stream_global->seen_error = true;
-    grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+    grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                        stream_global);
   }
   /* stream_global->recv_trailing_metadata_finished gives us a
      last chance replacement: we've received trailing metadata,
@@ -1658,7 +1590,8 @@
               grpc_mdstr_from_slice(gpr_slice_ref(*slice))));
     }
     stream_global->published_trailing_metadata = true;
-    grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+    grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                        stream_global);
   }
   if (slice) {
     gpr_slice_unref(*slice);
@@ -1718,7 +1651,8 @@
     GRPC_ERROR_UNREF(error);
     return;
   }
-  grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+  grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                      stream_global);
   if (close_reads && !stream_global->read_closed) {
     stream_global->read_closed_error = GRPC_ERROR_REF(error);
     stream_global->read_closed = true;
@@ -1899,6 +1833,10 @@
 
 static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
                             grpc_error *error) {
+  if (!grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, NULL)) {
+    error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS,
+                               GRPC_STATUS_UNAVAILABLE);
+  }
   close_transport_locked(exec_ctx, t, GRPC_ERROR_REF(error));
   end_all_the_calls(exec_ctx, t, error);
 }
@@ -1934,16 +1872,12 @@
  * INPUT PROCESSING - PARSING
  */
 
-static void reading_action_locked(grpc_exec_ctx *exec_ctx,
-                                  grpc_chttp2_transport *t,
-                                  grpc_chttp2_stream *s_unused, void *arg);
 static void parsing_action(grpc_exec_ctx *exec_ctx, void *arg,
                            grpc_error *error);
-static void post_reading_action_locked(grpc_exec_ctx *exec_ctx,
-                                       grpc_chttp2_transport *t,
-                                       grpc_chttp2_stream *s_unused, void *arg);
-static void post_parse_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
-                              grpc_chttp2_stream *s_unused, void *arg);
+static void post_reading_action_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                       grpc_error *error);
+static void post_parse_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                              grpc_error *error);
 
 static void reading_action(grpc_exec_ctx *exec_ctx, void *tp,
                            grpc_error *error) {
@@ -1951,16 +1885,20 @@
      reading_action_locked ->
        (parse_unlocked -> post_parse_locked)? ->
        post_reading_action_locked */
-  grpc_chttp2_run_with_global_lock(exec_ctx, tp, NULL, reading_action_locked,
-                                   GRPC_ERROR_REF(error), 0);
+  GPR_TIMER_BEGIN("reading_action", 0);
+  grpc_chttp2_transport *t = tp;
+  grpc_combiner_execute(exec_ctx, t->executor.combiner,
+                        &t->reading_action_locked, GRPC_ERROR_REF(error));
+  GPR_TIMER_END("reading_action", 0);
 }
 
-static void reading_action_locked(grpc_exec_ctx *exec_ctx,
-                                  grpc_chttp2_transport *t,
-                                  grpc_chttp2_stream *s_unused, void *arg) {
+static void reading_action_locked(grpc_exec_ctx *exec_ctx, void *tp,
+                                  grpc_error *error) {
+  GPR_TIMER_BEGIN("reading_action_locked", 0);
+
+  grpc_chttp2_transport *t = tp;
   grpc_chttp2_transport_global *transport_global = &t->global;
   grpc_chttp2_transport_parsing *transport_parsing = &t->parsing;
-  grpc_error *error = arg;
 
   GPR_ASSERT(!t->executor.parsing_active);
   if (!t->closed) {
@@ -1969,10 +1907,13 @@
     grpc_chttp2_stream_map_move_into(&t->new_stream_map,
                                      &t->parsing_stream_map);
     grpc_chttp2_prepare_to_read(transport_global, transport_parsing);
-    grpc_exec_ctx_sched(exec_ctx, &t->parsing_action, error, NULL);
+    grpc_exec_ctx_sched(exec_ctx, &t->parsing_action, GRPC_ERROR_REF(error),
+                        NULL);
   } else {
-    post_reading_action_locked(exec_ctx, t, s_unused, arg);
+    post_reading_action_locked(exec_ctx, t, error);
   }
+
+  GPR_TIMER_END("reading_action_locked", 0);
 }
 
 static grpc_error *try_http_parsing(grpc_exec_ctx *exec_ctx,
@@ -1987,7 +1928,8 @@
 
   grpc_error *parse_error = GRPC_ERROR_NONE;
   for (; i < t->read_buffer.count && parse_error == GRPC_ERROR_NONE; i++) {
-    parse_error = grpc_http_parser_parse(&parser, t->read_buffer.slices[i]);
+    parse_error =
+        grpc_http_parser_parse(&parser, t->read_buffer.slices[i], NULL);
   }
   if (parse_error == GRPC_ERROR_NONE &&
       (parse_error = grpc_http_parser_eof(&parser)) == GRPC_ERROR_NONE) {
@@ -2024,13 +1966,15 @@
   for (i = 0; i < GPR_ARRAY_SIZE(errors); i++) {
     GRPC_ERROR_UNREF(errors[i]);
   }
+  grpc_combiner_execute(exec_ctx, t->executor.combiner, &t->post_parse_locked,
+                        err);
   GPR_TIMER_END("reading_action.parse", 0);
-  grpc_chttp2_run_with_global_lock(exec_ctx, t, NULL, post_parse_locked, err,
-                                   0);
 }
 
-static void post_parse_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
-                              grpc_chttp2_stream *s_unused, void *arg) {
+static void post_parse_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                              grpc_error *error) {
+  GPR_TIMER_BEGIN("post_parse_locked", 0);
+  grpc_chttp2_transport *t = arg;
   grpc_chttp2_transport_global *transport_global = &t->global;
   grpc_chttp2_transport_parsing *transport_parsing = &t->parsing;
   /* copy parsing qbuf to global qbuf */
@@ -2056,7 +2000,7 @@
   if (t->post_parsing_op) {
     grpc_transport_op *op = t->post_parsing_op;
     t->post_parsing_op = NULL;
-    perform_transport_op_locked(exec_ctx, t, NULL, op);
+    perform_transport_op_locked(exec_ctx, op, GRPC_ERROR_NONE);
     gpr_free(op);
   }
   /* if a stream is in the stream map, and gets cancelled, we need to
@@ -2073,39 +2017,32 @@
     GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2");
   }
 
-  post_reading_action_locked(exec_ctx, t, s_unused, arg);
+  post_reading_action_locked(exec_ctx, t, error);
+  GPR_TIMER_END("post_parse_locked", 0);
 }
 
-static void post_reading_action_locked(grpc_exec_ctx *exec_ctx,
-                                       grpc_chttp2_transport *t,
-                                       grpc_chttp2_stream *s_unused,
-                                       void *arg) {
-  grpc_error *error = arg;
+static void post_reading_action_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                       grpc_error *error) {
+  GPR_TIMER_BEGIN("post_reading_action_locked", 0);
+  grpc_chttp2_transport *t = arg;
   bool keep_reading = false;
+  GRPC_ERROR_REF(error);
   if (error == GRPC_ERROR_NONE && t->closed) {
     error = GRPC_ERROR_CREATE("Transport closed");
   }
   if (error != GRPC_ERROR_NONE) {
-    if (!grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, NULL)) {
-      error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS,
-                                 GRPC_STATUS_UNAVAILABLE);
-    }
     drop_connection(exec_ctx, t, GRPC_ERROR_REF(error));
     t->endpoint_reading = 0;
     if (grpc_http_write_state_trace) {
       gpr_log(GPR_DEBUG, "R:%p -> 0 ws=%s", t,
               write_state_name(t->executor.write_state));
     }
-    if (t->executor.write_state == GRPC_CHTTP2_WRITING_INACTIVE && t->ep) {
-      destroy_endpoint(exec_ctx, t);
-    }
   } else if (!t->closed) {
     keep_reading = true;
     REF_TRANSPORT(t, "keep_reading");
     prevent_endpoint_shutdown(t);
   }
   gpr_slice_buffer_reset_and_unref(&t->read_buffer);
-  GRPC_ERROR_UNREF(error);
 
   if (keep_reading) {
     grpc_endpoint_read(exec_ctx, t->ep, &t->read_buffer, &t->reading_action);
@@ -2114,6 +2051,9 @@
   } else {
     UNREF_TRANSPORT(exec_ctx, t, "reading_action");
   }
+  GRPC_ERROR_UNREF(error);
+
+  GPR_TIMER_END("post_reading_action_locked", 0);
 }
 
 /*******************************************************************************
@@ -2135,36 +2075,16 @@
  * POLLSET STUFF
  */
 
-static void add_to_pollset_locked(grpc_exec_ctx *exec_ctx,
-                                  grpc_chttp2_transport *t,
-                                  grpc_chttp2_stream *s_unused, void *pollset) {
-  if (t->ep) {
-    grpc_endpoint_add_to_pollset(exec_ctx, t->ep, pollset);
-  }
-}
-
-static void add_to_pollset_set_locked(grpc_exec_ctx *exec_ctx,
-                                      grpc_chttp2_transport *t,
-                                      grpc_chttp2_stream *s_unused,
-                                      void *pollset_set) {
-  if (t->ep) {
-    grpc_endpoint_add_to_pollset_set(exec_ctx, t->ep, pollset_set);
-  }
-}
-
 static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
                         grpc_stream *gs, grpc_pollset *pollset) {
-  /* TODO(ctiller): keep pollset alive */
-  grpc_chttp2_run_with_global_lock(exec_ctx, (grpc_chttp2_transport *)gt,
-                                   (grpc_chttp2_stream *)gs,
-                                   add_to_pollset_locked, pollset, 0);
+  grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt;
+  grpc_endpoint_add_to_pollset(exec_ctx, t->ep, pollset);
 }
 
 static void set_pollset_set(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
                             grpc_stream *gs, grpc_pollset_set *pollset_set) {
-  grpc_chttp2_run_with_global_lock(exec_ctx, (grpc_chttp2_transport *)gt,
-                                   (grpc_chttp2_stream *)gs,
-                                   add_to_pollset_set_locked, pollset_set, 0);
+  grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt;
+  grpc_endpoint_add_to_pollset_set(exec_ctx, t->ep, pollset_set);
 }
 
 /*******************************************************************************
@@ -2176,6 +2096,7 @@
   if (gpr_unref(&bs->refs)) {
     GRPC_ERROR_UNREF(bs->error);
     gpr_slice_buffer_destroy(&bs->slices);
+    gpr_mu_destroy(&bs->slice_mu);
     gpr_free(bs);
   }
 }
@@ -2221,38 +2142,34 @@
   }
 }
 
-typedef struct {
-  grpc_chttp2_incoming_byte_stream *byte_stream;
-  gpr_slice *slice;
-  size_t max_size_hint;
-  grpc_closure *on_complete;
-} incoming_byte_stream_next_arg;
-
 static void incoming_byte_stream_next_locked(grpc_exec_ctx *exec_ctx,
-                                             grpc_chttp2_transport *t,
-                                             grpc_chttp2_stream *s,
-                                             void *argp) {
-  incoming_byte_stream_next_arg *arg = argp;
-  grpc_chttp2_incoming_byte_stream *bs =
-      (grpc_chttp2_incoming_byte_stream *)arg->byte_stream;
+                                             void *argp,
+                                             grpc_error *error_ignored) {
+  grpc_chttp2_incoming_byte_stream *bs = argp;
   grpc_chttp2_transport_global *transport_global = &bs->transport->global;
   grpc_chttp2_stream_global *stream_global = &bs->stream->global;
 
   if (bs->is_tail) {
-    incoming_byte_stream_update_flow_control(exec_ctx, transport_global,
-                                             stream_global, arg->max_size_hint,
-                                             bs->slices.length);
+    gpr_mu_lock(&bs->slice_mu);
+    size_t cur_length = bs->slices.length;
+    gpr_mu_unlock(&bs->slice_mu);
+    incoming_byte_stream_update_flow_control(
+        exec_ctx, transport_global, stream_global,
+        bs->next_action.max_size_hint, cur_length);
   }
+  gpr_mu_lock(&bs->slice_mu);
   if (bs->slices.count > 0) {
-    *arg->slice = gpr_slice_buffer_take_first(&bs->slices);
-    grpc_exec_ctx_sched(exec_ctx, arg->on_complete, GRPC_ERROR_NONE, NULL);
-  } else if (bs->error != GRPC_ERROR_NONE) {
-    grpc_exec_ctx_sched(exec_ctx, arg->on_complete, GRPC_ERROR_REF(bs->error),
+    *bs->next_action.slice = gpr_slice_buffer_take_first(&bs->slices);
+    grpc_exec_ctx_sched(exec_ctx, bs->next_action.on_complete, GRPC_ERROR_NONE,
                         NULL);
+  } else if (bs->error != GRPC_ERROR_NONE) {
+    grpc_exec_ctx_sched(exec_ctx, bs->next_action.on_complete,
+                        GRPC_ERROR_REF(bs->error), NULL);
   } else {
-    bs->on_next = arg->on_complete;
-    bs->next = arg->slice;
+    bs->on_next = bs->next_action.on_complete;
+    bs->next = bs->next_action.slice;
   }
+  gpr_mu_unlock(&bs->slice_mu);
   incoming_byte_stream_unref(exec_ctx, bs);
 }
 
@@ -2260,13 +2177,18 @@
                                      grpc_byte_stream *byte_stream,
                                      gpr_slice *slice, size_t max_size_hint,
                                      grpc_closure *on_complete) {
+  GPR_TIMER_BEGIN("incoming_byte_stream_next", 0);
   grpc_chttp2_incoming_byte_stream *bs =
       (grpc_chttp2_incoming_byte_stream *)byte_stream;
-  incoming_byte_stream_next_arg arg = {bs, slice, max_size_hint, on_complete};
   gpr_ref(&bs->refs);
-  grpc_chttp2_run_with_global_lock(exec_ctx, bs->transport, bs->stream,
-                                   incoming_byte_stream_next_locked, &arg,
-                                   sizeof(arg));
+  bs->next_action.slice = slice;
+  bs->next_action.max_size_hint = max_size_hint;
+  bs->next_action.on_complete = on_complete;
+  grpc_closure_init(&bs->next_action.closure, incoming_byte_stream_next_locked,
+                    bs);
+  grpc_combiner_execute(exec_ctx, bs->transport->executor.combiner,
+                        &bs->next_action.closure, GRPC_ERROR_NONE);
+  GPR_TIMER_END("incoming_byte_stream_next", 0);
   return 0;
 }
 
@@ -2274,9 +2196,8 @@
                                          grpc_byte_stream *byte_stream);
 
 static void incoming_byte_stream_destroy_locked(grpc_exec_ctx *exec_ctx,
-                                                grpc_chttp2_transport *t,
-                                                grpc_chttp2_stream *s,
-                                                void *byte_stream) {
+                                                void *byte_stream,
+                                                grpc_error *error_ignored) {
   grpc_chttp2_incoming_byte_stream *bs = byte_stream;
   GPR_ASSERT(bs->base.destroy == incoming_byte_stream_destroy);
   decrement_active_streams_locked(exec_ctx, &bs->transport->global,
@@ -2286,10 +2207,14 @@
 
 static void incoming_byte_stream_destroy(grpc_exec_ctx *exec_ctx,
                                          grpc_byte_stream *byte_stream) {
+  GPR_TIMER_BEGIN("incoming_byte_stream_destroy", 0);
   grpc_chttp2_incoming_byte_stream *bs =
       (grpc_chttp2_incoming_byte_stream *)byte_stream;
-  grpc_chttp2_run_with_global_lock(exec_ctx, bs->transport, bs->stream,
-                                   incoming_byte_stream_destroy_locked, bs, 0);
+  grpc_closure_init(&bs->destroy_action, incoming_byte_stream_destroy_locked,
+                    bs);
+  grpc_combiner_execute(exec_ctx, bs->transport->executor.combiner,
+                        &bs->destroy_action, GRPC_ERROR_NONE);
+  GPR_TIMER_END("incoming_byte_stream_destroy", 0);
 }
 
 typedef struct {
@@ -2297,90 +2222,45 @@
   gpr_slice slice;
 } incoming_byte_stream_push_arg;
 
-static void incoming_byte_stream_push_locked(grpc_exec_ctx *exec_ctx,
-                                             grpc_chttp2_transport *t,
-                                             grpc_chttp2_stream *s,
-                                             void *argp) {
-  incoming_byte_stream_push_arg *arg = argp;
-  grpc_chttp2_incoming_byte_stream *bs = arg->byte_stream;
-  if (bs->on_next != NULL) {
-    *bs->next = arg->slice;
-    grpc_exec_ctx_sched(exec_ctx, bs->on_next, GRPC_ERROR_NONE, NULL);
-    bs->on_next = NULL;
-  } else {
-    gpr_slice_buffer_add(&bs->slices, arg->slice);
-  }
-  incoming_byte_stream_unref(exec_ctx, bs);
-}
-
 void grpc_chttp2_incoming_byte_stream_push(grpc_exec_ctx *exec_ctx,
                                            grpc_chttp2_incoming_byte_stream *bs,
                                            gpr_slice slice) {
-  incoming_byte_stream_push_arg arg = {bs, slice};
-  gpr_ref(&bs->refs);
-  grpc_chttp2_run_with_global_lock(exec_ctx, bs->transport, bs->stream,
-                                   incoming_byte_stream_push_locked, &arg,
-                                   sizeof(arg));
+  gpr_mu_lock(&bs->slice_mu);
+  if (bs->on_next != NULL) {
+    *bs->next = slice;
+    grpc_exec_ctx_sched(exec_ctx, bs->on_next, GRPC_ERROR_NONE, NULL);
+    bs->on_next = NULL;
+  } else {
+    gpr_slice_buffer_add(&bs->slices, slice);
+  }
+  gpr_mu_unlock(&bs->slice_mu);
 }
 
-typedef struct {
-  grpc_chttp2_incoming_byte_stream *bs;
-  grpc_error *error;
-} bs_fail_args;
-
-static bs_fail_args *make_bs_fail_args(grpc_chttp2_incoming_byte_stream *bs,
-                                       grpc_error *error) {
-  bs_fail_args *a = gpr_malloc(sizeof(*a));
-  a->bs = bs;
-  a->error = error;
-  return a;
-}
-
-static void incoming_byte_stream_finished_failed_locked(
-    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, grpc_chttp2_stream *s,
-    void *argp) {
-  bs_fail_args *a = argp;
-  grpc_chttp2_incoming_byte_stream *bs = a->bs;
-  grpc_error *error = a->error;
-  gpr_free(a);
-  grpc_exec_ctx_sched(exec_ctx, bs->on_next, GRPC_ERROR_REF(error), NULL);
-  bs->on_next = NULL;
-  GRPC_ERROR_UNREF(bs->error);
-  bs->error = error;
-  incoming_byte_stream_unref(exec_ctx, bs);
-}
-
-static void incoming_byte_stream_finished_ok_locked(grpc_exec_ctx *exec_ctx,
-                                                    grpc_chttp2_transport *t,
-                                                    grpc_chttp2_stream *s,
-                                                    void *argp) {
-  grpc_chttp2_incoming_byte_stream *bs = argp;
+static void incoming_byte_stream_finished_locked(grpc_exec_ctx *exec_ctx,
+                                                 void *bsp, grpc_error *error) {
+  grpc_chttp2_incoming_byte_stream *bs = bsp;
+  if (error != GRPC_ERROR_NONE) {
+    grpc_exec_ctx_sched(exec_ctx, bs->on_next, GRPC_ERROR_REF(error), NULL);
+    bs->on_next = NULL;
+    GRPC_ERROR_UNREF(bs->error);
+    bs->error = error;
+  }
   incoming_byte_stream_unref(exec_ctx, bs);
 }
 
 void grpc_chttp2_incoming_byte_stream_finished(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs,
     grpc_error *error, int from_parsing_thread) {
+  GPR_TIMER_BEGIN("grpc_chttp2_incoming_byte_stream_finished", 0);
   if (from_parsing_thread) {
-    if (error == GRPC_ERROR_NONE) {
-      grpc_chttp2_run_with_global_lock(exec_ctx, bs->transport, bs->stream,
-                                       incoming_byte_stream_finished_ok_locked,
-                                       bs, 0);
-    } else {
-      grpc_chttp2_run_with_global_lock(
-          exec_ctx, bs->transport, bs->stream,
-          incoming_byte_stream_finished_failed_locked,
-          make_bs_fail_args(bs, error), 0);
-    }
+    grpc_closure_init(&bs->finished_action,
+                      incoming_byte_stream_finished_locked, bs);
+    grpc_combiner_execute(exec_ctx, bs->transport->executor.combiner,
+                          &bs->finished_action, GRPC_ERROR_REF(error));
   } else {
-    if (error == GRPC_ERROR_NONE) {
-      incoming_byte_stream_finished_ok_locked(exec_ctx, bs->transport,
-                                              bs->stream, bs);
-    } else {
-      incoming_byte_stream_finished_failed_locked(
-          exec_ctx, bs->transport, bs->stream, make_bs_fail_args(bs, error));
-    }
+    incoming_byte_stream_finished_locked(exec_ctx, bs, error);
   }
+  GPR_TIMER_END("grpc_chttp2_incoming_byte_stream_finished", 0);
 }
 
 grpc_chttp2_incoming_byte_stream *grpc_chttp2_incoming_byte_stream_create(
@@ -2393,6 +2273,7 @@
   incoming_byte_stream->base.flags = flags;
   incoming_byte_stream->base.next = incoming_byte_stream_next;
   incoming_byte_stream->base.destroy = incoming_byte_stream_destroy;
+  gpr_mu_init(&incoming_byte_stream->slice_mu);
   gpr_ref_init(&incoming_byte_stream->refs, 2);
   incoming_byte_stream->next_message = NULL;
   incoming_byte_stream->transport = TRANSPORT_FROM_PARSING(transport_parsing);
@@ -2538,9 +2419,12 @@
 
 void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx,
                                          grpc_transport *transport,
-                                         gpr_slice *slices, size_t nslices) {
+                                         gpr_slice_buffer *read_buffer) {
   grpc_chttp2_transport *t = (grpc_chttp2_transport *)transport;
   REF_TRANSPORT(t, "reading_action"); /* matches unref inside reading_action */
-  gpr_slice_buffer_addn(&t->read_buffer, slices, nslices);
+  if (read_buffer != NULL) {
+    gpr_slice_buffer_move_into(read_buffer, &t->read_buffer);
+    gpr_free(read_buffer);
+  }
   reading_action(exec_ctx, t, GRPC_ERROR_NONE);
 }
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.h b/src/core/ext/transport/chttp2/transport/chttp2_transport.h
index 5da4276..4e2d095 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.h
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.h
@@ -44,8 +44,10 @@
     grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args,
     grpc_endpoint *ep, int is_client);
 
+/// Takes ownership of \a read_buffer, which (if non-NULL) contains
+/// leftover bytes previously read from the endpoint (e.g., by handshakers).
 void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx,
                                          grpc_transport *transport,
-                                         gpr_slice *slices, size_t nslices);
+                                         gpr_slice_buffer *read_buffer);
 
 #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_TRANSPORT_H */
diff --git a/src/core/ext/transport/chttp2/transport/frame.h b/src/core/ext/transport/chttp2/transport/frame.h
index 7776609..507aae4 100644
--- a/src/core/ext/transport/chttp2/transport/frame.h
+++ b/src/core/ext/transport/chttp2/transport/frame.h
@@ -52,8 +52,6 @@
 #define GRPC_CHTTP2_FRAME_GOAWAY 7
 #define GRPC_CHTTP2_FRAME_WINDOW_UPDATE 8
 
-#define GRPC_CHTTP2_MAX_PAYLOAD_LENGTH ((1 << 14) - 1)
-
 #define GRPC_CHTTP2_DATA_FLAG_END_STREAM 1
 #define GRPC_CHTTP2_FLAG_ACK 1
 #define GRPC_CHTTP2_DATA_FLAG_END_HEADERS 4
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.c b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
index ebeee37..581471b 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
@@ -47,10 +47,10 @@
 
 #include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
 #include "src/core/ext/transport/chttp2/transport/hpack_table.h"
-#include "src/core/ext/transport/chttp2/transport/timeout_encoding.h"
 #include "src/core/ext/transport/chttp2/transport/varint.h"
 #include "src/core/lib/transport/metadata.h"
 #include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/timeout_encoding.h"
 
 #define HASH_FRAGMENT_1(x) ((x)&255)
 #define HASH_FRAGMENT_2(x) ((x >> 8) & 255)
@@ -78,6 +78,8 @@
   uint32_t stream_id;
   gpr_slice_buffer *output;
   grpc_transport_one_way_stats *stats;
+  /* maximum size of a frame */
+  size_t max_frame_size;
 } framer_state;
 
 /* fills p (which is expected to be 9 bytes long) with a data frame header */
@@ -123,7 +125,7 @@
    needed */
 static void ensure_space(framer_state *st, size_t need_bytes) {
   if (st->output->length - st->output_length_at_start_of_frame + need_bytes <=
-      GRPC_CHTTP2_MAX_PAYLOAD_LENGTH) {
+      st->max_frame_size) {
     return;
   }
   finish_frame(st, 0, 0);
@@ -149,8 +151,8 @@
   size_t len = GPR_SLICE_LENGTH(slice);
   size_t remaining;
   if (len == 0) return;
-  remaining = GRPC_CHTTP2_MAX_PAYLOAD_LENGTH +
-              st->output_length_at_start_of_frame - st->output->length;
+  remaining = st->max_frame_size + st->output_length_at_start_of_frame -
+              st->output->length;
   if (len <= remaining) {
     st->stats->header_bytes += len;
     gpr_slice_buffer_add(st->output, slice);
@@ -456,9 +458,9 @@
 
 static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline,
                          framer_state *st) {
-  char timeout_str[GRPC_CHTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
+  char timeout_str[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
   grpc_mdelem *mdelem;
-  grpc_chttp2_encode_timeout(
+  grpc_http2_encode_timeout(
       gpr_time_sub(deadline, gpr_now(deadline.clock_type)), timeout_str);
   mdelem = grpc_mdelem_from_metadata_strings(
       GRPC_MDSTR_GRPC_TIMEOUT, grpc_mdstr_from_string(timeout_str));
@@ -542,6 +544,7 @@
 void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c,
                                uint32_t stream_id,
                                grpc_metadata_batch *metadata, int is_eof,
+                               size_t max_frame_size,
                                grpc_transport_one_way_stats *stats,
                                gpr_slice_buffer *outbuf) {
   framer_state st;
@@ -555,6 +558,7 @@
   st.output = outbuf;
   st.is_first_frame = 1;
   st.stats = stats;
+  st.max_frame_size = max_frame_size;
 
   /* Encode a metadata batch; store the returned values, representing
      a metadata element that needs to be unreffed back into the metadata
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.h b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
index 0f7b0b0..4c3a931 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
@@ -91,6 +91,7 @@
 
 void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c, uint32_t id,
                                grpc_metadata_batch *metadata, int is_eof,
+                               size_t max_frame_size,
                                grpc_transport_one_way_stats *stats,
                                gpr_slice_buffer *outbuf);
 
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index e1dcf52..04b788b 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -48,6 +48,7 @@
 #include "src/core/ext/transport/chttp2/transport/hpack_parser.h"
 #include "src/core/ext/transport/chttp2/transport/incoming_metadata.h"
 #include "src/core/ext/transport/chttp2/transport/stream_map.h"
+#include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/transport/connectivity_state.h"
 #include "src/core/lib/transport/transport_impl.h"
@@ -161,9 +162,20 @@
   grpc_chttp2_transport *transport;
   grpc_chttp2_stream *stream;
   int is_tail;
+
+  gpr_mu slice_mu;  // protects slices, on_next
   gpr_slice_buffer slices;
   grpc_closure *on_next;
   gpr_slice *next;
+
+  struct {
+    grpc_closure closure;
+    gpr_slice *slice;
+    size_t max_size_hint;
+    grpc_closure *on_complete;
+  } next_action;
+  grpc_closure destroy_action;
+  grpc_closure finished_action;
 };
 
 typedef struct {
@@ -223,6 +235,8 @@
   uint8_t is_client;
   /** callback for when writing is done */
   grpc_closure done_cb;
+  /** maximum frame size */
+  uint32_t max_frame_size;
 } grpc_chttp2_transport_writing;
 
 struct grpc_chttp2_transport_parsing {
@@ -294,23 +308,11 @@
   int64_t outgoing_window;
 };
 
-typedef void (*grpc_chttp2_locked_action)(grpc_exec_ctx *ctx,
-                                          grpc_chttp2_transport *t,
-                                          grpc_chttp2_stream *s, void *arg);
-
-typedef struct grpc_chttp2_executor_action_header {
-  grpc_chttp2_stream *stream;
-  grpc_chttp2_locked_action action;
-  struct grpc_chttp2_executor_action_header *next;
-  void *arg;
-} grpc_chttp2_executor_action_header;
-
 typedef enum {
+  /** no writing activity allowed */
+  GRPC_CHTTP2_WRITES_CORKED,
   /** no writing activity */
   GRPC_CHTTP2_WRITING_INACTIVE,
-  /** write has been requested, but not scheduled yet */
-  GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER,
-  GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER,
   /** write has been requested and scheduled against the workqueue */
   GRPC_CHTTP2_WRITE_SCHEDULED,
   /** write has been initiated after being reaped from the workqueue */
@@ -331,7 +333,7 @@
   gpr_refcount shutdown_ep_refs;
 
   struct {
-    gpr_mu mu;
+    grpc_combiner *combiner;
 
     /** is a thread currently in the global lock */
     bool global_active;
@@ -339,9 +341,8 @@
     bool parsing_active;
     /** write execution state of the transport */
     grpc_chttp2_write_state write_state;
-
-    grpc_chttp2_executor_action_header *pending_actions_head;
-    grpc_chttp2_executor_action_header *pending_actions_tail;
+    /** has a check_read_ops been scheduled */
+    bool check_read_ops_scheduled;
   } executor;
 
   /** is the transport destroying itself? */
@@ -378,10 +379,16 @@
   grpc_closure writing_action;
   /** closure to start reading from the endpoint */
   grpc_closure reading_action;
+  grpc_closure reading_action_locked;
+  grpc_closure post_parse_locked;
   /** closure to actually do parsing */
   grpc_closure parsing_action;
   /** closure to initiate writing */
   grpc_closure initiate_writing;
+  /** closure to finish writing */
+  grpc_closure terminate_writing;
+  /** closure to flush read state up the stack */
+  grpc_closure initiate_read_flush_locked;
 
   /** incoming read bytes */
   gpr_slice_buffer read_buffer;
@@ -525,11 +532,16 @@
 };
 
 struct grpc_chttp2_stream {
+  grpc_chttp2_transport *t;
   grpc_stream_refcount *refcount;
   grpc_chttp2_stream_global global;
   grpc_chttp2_stream_writing writing;
   grpc_chttp2_stream_parsing parsing;
 
+  grpc_closure init_stream;
+  grpc_closure destroy_stream;
+  void *destroy_stream_arg;
+
   grpc_chttp2_stream_link links[STREAM_LIST_COUNT];
   uint8_t included[STREAM_LIST_COUNT];
 };
@@ -624,7 +636,7 @@
     grpc_chttp2_stream_global **stream_global);
 
 void grpc_chttp2_list_add_check_read_ops(
-    grpc_chttp2_transport_global *transport_global,
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
     grpc_chttp2_stream_global *stream_global);
 bool grpc_chttp2_list_remove_check_read_ops(
     grpc_chttp2_transport_global *transport_global,
@@ -704,12 +716,6 @@
     grpc_chttp2_stream_global *stream_global, grpc_closure **pclosure,
     grpc_error *error);
 
-void grpc_chttp2_run_with_global_lock(grpc_exec_ctx *exec_ctx,
-                                      grpc_chttp2_transport *transport,
-                                      grpc_chttp2_stream *optional_stream,
-                                      grpc_chttp2_locked_action action,
-                                      void *arg, size_t sizeof_arg);
-
 #define GRPC_CHTTP2_CLIENT_CONNECT_STRING "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
 #define GRPC_CHTTP2_CLIENT_CONNECT_STRLEN \
   (sizeof(GRPC_CHTTP2_CLIENT_CONNECT_STRING) - 1)
diff --git a/src/core/ext/transport/chttp2/transport/parsing.c b/src/core/ext/transport/chttp2/transport/parsing.c
index e1fc0dd..0e6d579 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.c
+++ b/src/core/ext/transport/chttp2/transport/parsing.c
@@ -41,9 +41,9 @@
 
 #include "src/core/ext/transport/chttp2/transport/http2_errors.h"
 #include "src/core/ext/transport/chttp2/transport/status_conversion.h"
-#include "src/core/ext/transport/chttp2/transport/timeout_encoding.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/timeout_encoding.h"
 
 #define TRANSPORT_FROM_PARSING(tp)                                        \
   ((grpc_chttp2_transport *)((char *)(tp)-offsetof(grpc_chttp2_transport, \
@@ -177,7 +177,8 @@
       stream_global->seen_error = true;
       stream_global->exceeded_metadata_size =
           stream_parsing->exceeded_metadata_size;
-      grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+      grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                          stream_global);
     }
 
     /* flush stats to global stream state */
@@ -203,7 +204,8 @@
       stream_global->incoming_frames.tail->is_tail = 0;
     }
     if (stream_parsing->data_parser.incoming_frames.head != NULL) {
-      grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+      grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                          stream_global);
     }
     grpc_chttp2_incoming_frame_queue_merge(
         &stream_global->incoming_frames,
@@ -219,7 +221,8 @@
       GPR_SWAP(grpc_chttp2_incoming_metadata_buffer,
                stream_parsing->metadata_buffer[0],
                stream_global->received_initial_metadata);
-      grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+      grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                          stream_global);
     }
     if (!stream_global->published_trailing_metadata &&
         stream_parsing->got_metadata_on_parse[1]) {
@@ -228,7 +231,8 @@
       GPR_SWAP(grpc_chttp2_incoming_metadata_buffer,
                stream_parsing->metadata_buffer[1],
                stream_global->received_trailing_metadata);
-      grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
+      grpc_chttp2_list_add_check_read_ops(exec_ctx, transport_global,
+                                          stream_global);
     }
 
     if (stream_parsing->forced_close_error != GRPC_ERROR_NONE) {
@@ -668,8 +672,8 @@
     if (!cached_timeout) {
       /* not already parsed: parse it now, and store the result away */
       cached_timeout = gpr_malloc(sizeof(gpr_timespec));
-      if (!grpc_chttp2_decode_timeout(grpc_mdstr_as_c_string(md->value),
-                                      cached_timeout)) {
+      if (!grpc_http2_decode_timeout(grpc_mdstr_as_c_string(md->value),
+                                     cached_timeout)) {
         gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'",
                 grpc_mdstr_as_c_string(md->value));
         *cached_timeout = gpr_inf_future(GPR_TIMESPAN);
diff --git a/src/core/ext/transport/chttp2/transport/stream_lists.c b/src/core/ext/transport/chttp2/transport/stream_lists.c
index 2eb5f5f..4dc4968 100644
--- a/src/core/ext/transport/chttp2/transport/stream_lists.c
+++ b/src/core/ext/transport/chttp2/transport/stream_lists.c
@@ -298,8 +298,15 @@
 }
 
 void grpc_chttp2_list_add_check_read_ops(
-    grpc_chttp2_transport_global *transport_global,
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
     grpc_chttp2_stream_global *stream_global) {
+  grpc_chttp2_transport *t = TRANSPORT_FROM_GLOBAL(transport_global);
+  if (!t->executor.check_read_ops_scheduled) {
+    grpc_combiner_execute_finally(exec_ctx, t->executor.combiner,
+                                  &t->initiate_read_flush_locked,
+                                  GRPC_ERROR_NONE, false);
+    t->executor.check_read_ops_scheduled = true;
+  }
   stream_list_add(TRANSPORT_FROM_GLOBAL(transport_global),
                   STREAM_FROM_GLOBAL(stream_global),
                   GRPC_CHTTP2_LIST_CHECK_READ_OPS);
diff --git a/src/core/ext/transport/chttp2/transport/timeout_encoding.c b/src/core/ext/transport/chttp2/transport/timeout_encoding.c
deleted file mode 100644
index b7f7912..0000000
--- a/src/core/ext/transport/chttp2/transport/timeout_encoding.c
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include "src/core/ext/transport/chttp2/transport/timeout_encoding.h"
-
-#include <stdio.h>
-#include <string.h>
-
-#include <grpc/support/port_platform.h>
-#include "src/core/lib/support/string.h"
-
-static int64_t round_up(int64_t x, int64_t divisor) {
-  return (x / divisor + (x % divisor != 0)) * divisor;
-}
-
-/* round an integer up to the next value with three significant figures */
-static int64_t round_up_to_three_sig_figs(int64_t x) {
-  if (x < 1000) return x;
-  if (x < 10000) return round_up(x, 10);
-  if (x < 100000) return round_up(x, 100);
-  if (x < 1000000) return round_up(x, 1000);
-  if (x < 10000000) return round_up(x, 10000);
-  if (x < 100000000) return round_up(x, 100000);
-  if (x < 1000000000) return round_up(x, 1000000);
-  return round_up(x, 10000000);
-}
-
-/* encode our minimum viable timeout value */
-static void enc_tiny(char *buffer) { memcpy(buffer, "1n", 3); }
-
-static void enc_ext(char *buffer, int64_t value, char ext) {
-  int n = int64_ttoa(value, buffer);
-  buffer[n] = ext;
-  buffer[n + 1] = 0;
-}
-
-static void enc_seconds(char *buffer, int64_t sec) {
-  if (sec % 3600 == 0) {
-    enc_ext(buffer, sec / 3600, 'H');
-  } else if (sec % 60 == 0) {
-    enc_ext(buffer, sec / 60, 'M');
-  } else {
-    enc_ext(buffer, sec, 'S');
-  }
-}
-
-static void enc_nanos(char *buffer, int64_t x) {
-  x = round_up_to_three_sig_figs(x);
-  if (x < 100000) {
-    if (x % 1000 == 0) {
-      enc_ext(buffer, x / 1000, 'u');
-    } else {
-      enc_ext(buffer, x, 'n');
-    }
-  } else if (x < 100000000) {
-    if (x % 1000000 == 0) {
-      enc_ext(buffer, x / 1000000, 'm');
-    } else {
-      enc_ext(buffer, x / 1000, 'u');
-    }
-  } else if (x < 1000000000) {
-    enc_ext(buffer, x / 1000000, 'm');
-  } else {
-    /* note that this is only ever called with times of less than one second,
-       so if we reach here the time must have been rounded up to a whole second
-       (and no more) */
-    memcpy(buffer, "1S", 3);
-  }
-}
-
-static void enc_micros(char *buffer, int64_t x) {
-  x = round_up_to_three_sig_figs(x);
-  if (x < 100000) {
-    if (x % 1000 == 0) {
-      enc_ext(buffer, x / 1000, 'm');
-    } else {
-      enc_ext(buffer, x, 'u');
-    }
-  } else if (x < 100000000) {
-    if (x % 1000000 == 0) {
-      enc_ext(buffer, x / 1000000, 'S');
-    } else {
-      enc_ext(buffer, x / 1000, 'm');
-    }
-  } else {
-    enc_ext(buffer, x / 1000000, 'S');
-  }
-}
-
-void grpc_chttp2_encode_timeout(gpr_timespec timeout, char *buffer) {
-  if (timeout.tv_sec < 0) {
-    enc_tiny(buffer);
-  } else if (timeout.tv_sec == 0) {
-    enc_nanos(buffer, timeout.tv_nsec);
-  } else if (timeout.tv_sec < 1000 && timeout.tv_nsec != 0) {
-    enc_micros(buffer,
-               (int64_t)(timeout.tv_sec * 1000000) +
-                   (timeout.tv_nsec / 1000 + (timeout.tv_nsec % 1000 != 0)));
-  } else {
-    enc_seconds(buffer, timeout.tv_sec + (timeout.tv_nsec != 0));
-  }
-}
-
-static int is_all_whitespace(const char *p) {
-  while (*p == ' ') p++;
-  return *p == 0;
-}
-
-int grpc_chttp2_decode_timeout(const char *buffer, gpr_timespec *timeout) {
-  int32_t x = 0;
-  const uint8_t *p = (const uint8_t *)buffer;
-  int have_digit = 0;
-  /* skip whitespace */
-  for (; *p == ' '; p++)
-    ;
-  /* decode numeric part */
-  for (; *p >= '0' && *p <= '9'; p++) {
-    int32_t digit = (int32_t)(*p - (uint8_t)'0');
-    have_digit = 1;
-    /* spec allows max. 8 digits, but we allow values up to 1,000,000,000 */
-    if (x >= (100 * 1000 * 1000)) {
-      if (x != (100 * 1000 * 1000) || digit != 0) {
-        *timeout = gpr_inf_future(GPR_TIMESPAN);
-        return 1;
-      }
-    }
-    x = x * 10 + digit;
-  }
-  if (!have_digit) return 0;
-  /* skip whitespace */
-  for (; *p == ' '; p++)
-    ;
-  /* decode unit specifier */
-  switch (*p) {
-    case 'n':
-      *timeout = gpr_time_from_nanos(x, GPR_TIMESPAN);
-      break;
-    case 'u':
-      *timeout = gpr_time_from_micros(x, GPR_TIMESPAN);
-      break;
-    case 'm':
-      *timeout = gpr_time_from_millis(x, GPR_TIMESPAN);
-      break;
-    case 'S':
-      *timeout = gpr_time_from_seconds(x, GPR_TIMESPAN);
-      break;
-    case 'M':
-      *timeout = gpr_time_from_minutes(x, GPR_TIMESPAN);
-      break;
-    case 'H':
-      *timeout = gpr_time_from_hours(x, GPR_TIMESPAN);
-      break;
-    default:
-      return 0;
-  }
-  p++;
-  return is_all_whitespace((const char *)p);
-}
diff --git a/src/core/ext/transport/chttp2/transport/timeout_encoding.h b/src/core/ext/transport/chttp2/transport/timeout_encoding.h
deleted file mode 100644
index df2324c..0000000
--- a/src/core/ext/transport/chttp2/transport/timeout_encoding.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_TIMEOUT_ENCODING_H
-#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_TIMEOUT_ENCODING_H
-
-#include <grpc/support/time.h>
-#include "src/core/lib/support/string.h"
-
-#define GRPC_CHTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE (GPR_LTOA_MIN_BUFSIZE + 1)
-
-/* Encode/decode timeouts to the GRPC over HTTP2 format;
-   encoding may round up arbitrarily */
-void grpc_chttp2_encode_timeout(gpr_timespec timeout, char *buffer);
-int grpc_chttp2_decode_timeout(const char *buffer, gpr_timespec *timeout);
-
-#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_TIMEOUT_ENCODING_H */
diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c
index e0d8772..979515b 100644
--- a/src/core/ext/transport/chttp2/transport/writing.c
+++ b/src/core/ext/transport/chttp2/transport/writing.c
@@ -51,14 +51,9 @@
 
   GPR_TIMER_BEGIN("grpc_chttp2_unlocking_check_writes", 0);
 
-  /* simple writes are queued to qbuf, and flushed here */
-  gpr_slice_buffer_swap(&transport_global->qbuf, &transport_writing->outbuf);
-  GPR_ASSERT(transport_global->qbuf.count == 0);
-
-  grpc_chttp2_hpack_compressor_set_max_table_size(
-      &transport_writing->hpack_compressor,
-      transport_global->settings[GRPC_PEER_SETTINGS]
-                                [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]);
+  transport_writing->max_frame_size =
+      transport_global->settings[GRPC_ACKED_SETTINGS]
+                                [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE];
 
   if (transport_global->dirtied_local_settings &&
       !transport_global->sent_local_settings) {
@@ -73,6 +68,16 @@
     transport_global->sent_local_settings = 1;
   }
 
+  /* simple writes are queued to qbuf, and flushed here */
+  gpr_slice_buffer_move_into(&transport_global->qbuf,
+                             &transport_writing->outbuf);
+  GPR_ASSERT(transport_global->qbuf.count == 0);
+
+  grpc_chttp2_hpack_compressor_set_max_table_size(
+      &transport_writing->hpack_compressor,
+      transport_global->settings[GRPC_PEER_SETTINGS]
+                                [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]);
+
   GRPC_CHTTP2_FLOW_MOVE_TRANSPORT("write", transport_writing, outgoing_window,
                                   transport_global, outgoing_window);
   if (transport_writing->outgoing_window > 0) {
@@ -202,17 +207,19 @@
 
   GPR_TIMER_BEGIN("finalize_outbuf", 0);
 
+  bool is_first_data_frame = true;
   while (
       grpc_chttp2_list_pop_writing_stream(transport_writing, &stream_writing)) {
     uint32_t max_outgoing =
-        (uint32_t)GPR_MIN(GRPC_CHTTP2_MAX_PAYLOAD_LENGTH,
+        (uint32_t)GPR_MIN(transport_writing->max_frame_size,
                           GPR_MIN(stream_writing->outgoing_window,
                                   transport_writing->outgoing_window));
     /* send initial metadata if it's available */
     if (stream_writing->send_initial_metadata != NULL) {
       grpc_chttp2_encode_header(
           &transport_writing->hpack_compressor, stream_writing->id,
-          stream_writing->send_initial_metadata, 0, &stream_writing->stats,
+          stream_writing->send_initial_metadata, 0,
+          transport_writing->max_frame_size, &stream_writing->stats,
           &transport_writing->outbuf);
       stream_writing->send_initial_metadata = NULL;
       stream_writing->sent_initial_metadata = 1;
@@ -266,6 +273,11 @@
             stream_writing->id, &stream_writing->flow_controlled_buffer,
             send_bytes, is_last_frame, &stream_writing->stats,
             &transport_writing->outbuf);
+        if (is_first_data_frame) {
+          /* TODO(dgq): this is a hack. It'll be fix in a future refactoring */
+          stream_writing->stats.data_bytes -= 5; /* discount grpc framing */
+          is_first_data_frame = false;
+        }
         GRPC_CHTTP2_FLOW_DEBIT_STREAM("write", transport_writing,
                                       stream_writing, outgoing_window,
                                       send_bytes);
@@ -297,7 +309,8 @@
       } else {
         grpc_chttp2_encode_header(
             &transport_writing->hpack_compressor, stream_writing->id,
-            stream_writing->send_trailing_metadata, 1, &stream_writing->stats,
+            stream_writing->send_trailing_metadata, 1,
+            transport_writing->max_frame_size, &stream_writing->stats,
             &transport_writing->outbuf);
       }
       if (!transport_writing->is_client && !stream_writing->read_closed) {
@@ -332,6 +345,7 @@
 void grpc_chttp2_cleanup_writing(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
     grpc_chttp2_transport_writing *transport_writing) {
+  GPR_TIMER_BEGIN("grpc_chttp2_cleanup_writing", 0);
   grpc_chttp2_stream_writing *stream_writing;
   grpc_chttp2_stream_global *stream_global;
 
@@ -370,4 +384,5 @@
     GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2_writing");
   }
   gpr_slice_buffer_reset_and_unref(&transport_writing->outbuf);
+  GPR_TIMER_END("grpc_chttp2_cleanup_writing", 0);
 }
diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.c b/src/core/ext/transport/cronet/transport/cronet_transport.c
index 25d8aca..366690a 100644
--- a/src/core/ext/transport/cronet/transport/cronet_transport.c
+++ b/src/core/ext/transport/cronet/transport/cronet_transport.c
@@ -46,189 +46,537 @@
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/transport/metadata_batch.h"
+#include "src/core/lib/transport/static_metadata.h"
 #include "src/core/lib/transport/transport_impl.h"
 #include "third_party/objective_c/Cronet/cronet_c_for_grpc.h"
 
 #define GRPC_HEADER_SIZE_IN_BYTES 5
 
-// Global flag that gets set with GRPC_TRACE env variable
-int grpc_cronet_trace = 1;
+#define CRONET_LOG(...)                          \
+  do {                                           \
+    if (grpc_cronet_trace) gpr_log(__VA_ARGS__); \
+  } while (0)
 
-// Cronet transport object
+/* TODO (makdharma): Hook up into the wider tracing mechanism */
+int grpc_cronet_trace = 0;
+
+enum e_op_result {
+  ACTION_TAKEN_WITH_CALLBACK,
+  ACTION_TAKEN_NO_CALLBACK,
+  NO_ACTION_POSSIBLE
+};
+
+enum e_op_id {
+  OP_SEND_INITIAL_METADATA = 0,
+  OP_SEND_MESSAGE,
+  OP_SEND_TRAILING_METADATA,
+  OP_RECV_MESSAGE,
+  OP_RECV_INITIAL_METADATA,
+  OP_RECV_TRAILING_METADATA,
+  OP_CANCEL_ERROR,
+  OP_ON_COMPLETE,
+  OP_FAILED,
+  OP_SUCCEEDED,
+  OP_CANCELED,
+  OP_RECV_MESSAGE_AND_ON_COMPLETE,
+  OP_READ_REQ_MADE,
+  OP_NUM_OPS
+};
+
+/* Cronet callbacks. See cronet_c_for_grpc.h for documentation for each. */
+
+static void on_request_headers_sent(cronet_bidirectional_stream *);
+static void on_response_headers_received(
+    cronet_bidirectional_stream *,
+    const cronet_bidirectional_stream_header_array *, const char *);
+static void on_write_completed(cronet_bidirectional_stream *, const char *);
+static void on_read_completed(cronet_bidirectional_stream *, char *, int);
+static void on_response_trailers_received(
+    cronet_bidirectional_stream *,
+    const cronet_bidirectional_stream_header_array *);
+static void on_succeeded(cronet_bidirectional_stream *);
+static void on_failed(cronet_bidirectional_stream *, int);
+static void on_canceled(cronet_bidirectional_stream *);
+static cronet_bidirectional_stream_callback cronet_callbacks = {
+    on_request_headers_sent,
+    on_response_headers_received,
+    on_read_completed,
+    on_write_completed,
+    on_response_trailers_received,
+    on_succeeded,
+    on_failed,
+    on_canceled};
+
+/* Cronet transport object */
 struct grpc_cronet_transport {
   grpc_transport base; /* must be first element in this structure */
   cronet_engine *engine;
   char *host;
 };
-
 typedef struct grpc_cronet_transport grpc_cronet_transport;
 
-enum send_state {
-  CRONET_SEND_IDLE = 0,
-  CRONET_REQ_STARTED,
-  CRONET_SEND_HEADER,
-  CRONET_WRITE,
-  CRONET_WRITE_COMPLETED,
+/* TODO (makdharma): reorder structure for memory efficiency per
+   http://www.catb.org/esr/structure-packing/#_structure_reordering: */
+struct read_state {
+  /* vars to store data coming from server */
+  char *read_buffer;
+  bool length_field_received;
+  int received_bytes;
+  int remaining_bytes;
+  int length_field;
+  char grpc_header_bytes[GRPC_HEADER_SIZE_IN_BYTES];
+  char *payload_field;
+  bool read_stream_closed;
+
+  /* vars for holding data destined for the application */
+  struct grpc_slice_buffer_stream sbs;
+  gpr_slice_buffer read_slice_buffer;
+
+  /* vars for trailing metadata */
+  grpc_chttp2_incoming_metadata_buffer trailing_metadata;
+  bool trailing_metadata_valid;
+
+  /* vars for initial metadata */
+  grpc_chttp2_incoming_metadata_buffer initial_metadata;
 };
 
-enum recv_state {
-  CRONET_RECV_IDLE = 0,
-  CRONET_RECV_READ_LENGTH,
-  CRONET_RECV_READ_DATA,
-  CRONET_RECV_CLOSED,
+struct write_state {
+  char *write_buffer;
 };
 
-static const char *recv_state_name[] = {
-    "CRONET_RECV_IDLE", "CRONET_RECV_READ_LENGTH", "CRONET_RECV_READ_DATA,",
-    "CRONET_RECV_CLOSED"};
-
-// Enum that identifies calling function.
-enum e_caller {
-  PERFORM_STREAM_OP,
-  ON_READ_COMPLETE,
-  ON_RESPONSE_HEADERS_RECEIVED,
-  ON_RESPONSE_TRAILERS_RECEIVED
+/* track state of one stream op */
+struct op_state {
+  bool state_op_done[OP_NUM_OPS];
+  bool state_callback_received[OP_NUM_OPS];
+  /* data structure for storing data coming from server */
+  struct read_state rs;
+  /* data structure for storing data going to the server */
+  struct write_state ws;
 };
 
-enum callback_id {
-  CB_SEND_INITIAL_METADATA = 0,
-  CB_SEND_MESSAGE,
-  CB_SEND_TRAILING_METADATA,
-  CB_RECV_MESSAGE,
-  CB_RECV_INITIAL_METADATA,
-  CB_RECV_TRAILING_METADATA,
-  CB_NUM_CALLBACKS
+struct op_and_state {
+  grpc_transport_stream_op op;
+  struct op_state state;
+  bool done;
+  struct stream_obj *s;      /* Pointer back to the stream object */
+  struct op_and_state *next; /* next op_and_state in the linked list */
+};
+
+struct op_storage {
+  int num_pending_ops;
+  struct op_and_state *head;
 };
 
 struct stream_obj {
-  // we store received bytes here as they trickle in.
-  gpr_slice_buffer write_slice_buffer;
+  struct op_and_state *oas;
+  grpc_transport_stream_op *curr_op;
+  grpc_cronet_transport curr_ct;
+  grpc_stream *curr_gs;
   cronet_bidirectional_stream *cbs;
-  gpr_slice slice;
-  gpr_slice_buffer read_slice_buffer;
-  struct grpc_slice_buffer_stream sbs;
-  char *read_buffer;
-  int remaining_read_bytes;
-  int total_read_bytes;
-
-  char *write_buffer;
-  size_t write_buffer_size;
-
-  // Hold the URL
-  char *url;
-
-  bool response_headers_received;
-  bool read_requested;
-  bool response_trailers_received;
-  bool read_closed;
-
-  // Recv message stuff
-  grpc_byte_buffer **recv_message;
-  // Initial metadata stuff
-  grpc_metadata_batch *recv_initial_metadata;
-  // Trailing metadata stuff
-  grpc_metadata_batch *recv_trailing_metadata;
-  grpc_chttp2_incoming_metadata_buffer imb;
-
-  // This mutex protects receive state machine execution
-  gpr_mu recv_mu;
-  // we can queue up up to 2 callbacks for each OP
-  grpc_closure *callback_list[CB_NUM_CALLBACKS][2];
-
-  // storage for header
-  cronet_bidirectional_stream_header *headers;
-  uint32_t num_headers;
   cronet_bidirectional_stream_header_array header_array;
-  // state tracking
-  enum recv_state cronet_recv_state;
-  enum send_state cronet_send_state;
-};
 
+  /* Stream level state. Some state will be tracked both at stream and stream_op
+   * level */
+  struct op_state state;
+
+  /* OP storage */
+  struct op_storage storage;
+
+  /* Mutex to protect storage */
+  gpr_mu mu;
+};
 typedef struct stream_obj stream_obj;
 
-static void next_send_step(stream_obj *s);
-static void next_recv_step(stream_obj *s, enum e_caller caller);
+static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
+                                          struct op_and_state *oas);
 
-static void set_pollset_do_nothing(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
-                                   grpc_stream *gs, grpc_pollset *pollset) {}
+/*
+  Utility function to translate enum into string for printing
+*/
+static const char *op_result_string(enum e_op_result i) {
+  switch (i) {
+    case ACTION_TAKEN_WITH_CALLBACK:
+      return "ACTION_TAKEN_WITH_CALLBACK";
+    case ACTION_TAKEN_NO_CALLBACK:
+      return "ACTION_TAKEN_NO_CALLBACK";
+    case NO_ACTION_POSSIBLE:
+      return "NO_ACTION_POSSIBLE";
+  }
+  GPR_UNREACHABLE_CODE(return "UNKNOWN");
+}
 
-static void set_pollset_set_do_nothing(grpc_exec_ctx *exec_ctx,
-                                       grpc_transport *gt, grpc_stream *gs,
-                                       grpc_pollset_set *pollset_set) {}
+static const char *op_id_string(enum e_op_id i) {
+  switch (i) {
+    case OP_SEND_INITIAL_METADATA:
+      return "OP_SEND_INITIAL_METADATA";
+    case OP_SEND_MESSAGE:
+      return "OP_SEND_MESSAGE";
+    case OP_SEND_TRAILING_METADATA:
+      return "OP_SEND_TRAILING_METADATA";
+    case OP_RECV_MESSAGE:
+      return "OP_RECV_MESSAGE";
+    case OP_RECV_INITIAL_METADATA:
+      return "OP_RECV_INITIAL_METADATA";
+    case OP_RECV_TRAILING_METADATA:
+      return "OP_RECV_TRAILING_METADATA";
+    case OP_CANCEL_ERROR:
+      return "OP_CANCEL_ERROR";
+    case OP_ON_COMPLETE:
+      return "OP_ON_COMPLETE";
+    case OP_FAILED:
+      return "OP_FAILED";
+    case OP_SUCCEEDED:
+      return "OP_SUCCEEDED";
+    case OP_CANCELED:
+      return "OP_CANCELED";
+    case OP_RECV_MESSAGE_AND_ON_COMPLETE:
+      return "OP_RECV_MESSAGE_AND_ON_COMPLETE";
+    case OP_READ_REQ_MADE:
+      return "OP_READ_REQ_MADE";
+    case OP_NUM_OPS:
+      return "OP_NUM_OPS";
+  }
+  return "UNKNOWN";
+}
 
-static void enqueue_callbacks(grpc_closure *callback_list[]) {
+/*
+  Add a new stream op to op storage.
+*/
+static void add_to_storage(struct stream_obj *s, grpc_transport_stream_op *op) {
+  struct op_storage *storage = &s->storage;
+  /* add new op at the beginning of the linked list. The memory is freed
+  in remove_from_storage */
+  struct op_and_state *new_op = gpr_malloc(sizeof(struct op_and_state));
+  memcpy(&new_op->op, op, sizeof(grpc_transport_stream_op));
+  memset(&new_op->state, 0, sizeof(new_op->state));
+  new_op->s = s;
+  new_op->done = false;
+  gpr_mu_lock(&s->mu);
+  new_op->next = storage->head;
+  storage->head = new_op;
+  storage->num_pending_ops++;
+  CRONET_LOG(GPR_DEBUG, "adding new op %p. %d in the queue.", new_op,
+             storage->num_pending_ops);
+  gpr_mu_unlock(&s->mu);
+}
+
+/*
+  Traverse the linked list and delete op and free memory
+*/
+static void remove_from_storage(struct stream_obj *s,
+                                struct op_and_state *oas) {
+  struct op_and_state *curr;
+  if (s->storage.head == NULL || oas == NULL) {
+    return;
+  }
+  if (s->storage.head == oas) {
+    s->storage.head = oas->next;
+    gpr_free(oas);
+    s->storage.num_pending_ops--;
+    CRONET_LOG(GPR_DEBUG, "Freed %p. Now %d in the queue", oas,
+               s->storage.num_pending_ops);
+  } else {
+    for (curr = s->storage.head; curr != NULL; curr = curr->next) {
+      if (curr->next == oas) {
+        curr->next = oas->next;
+        s->storage.num_pending_ops--;
+        CRONET_LOG(GPR_DEBUG, "Freed %p. Now %d in the queue", oas,
+                   s->storage.num_pending_ops);
+        gpr_free(oas);
+        break;
+      } else if (curr->next == NULL) {
+        CRONET_LOG(GPR_ERROR, "Reached end of LL and did not find op to free");
+      }
+    }
+  }
+}
+
+/*
+  Cycle through ops and try to take next action. Break when either
+  an action with callback is taken, or no action is possible.
+  This can get executed from the Cronet network thread via cronet callback
+  or on the application supplied thread via the perform_stream_op function.
+*/
+static void execute_from_storage(stream_obj *s) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  if (callback_list[0]) {
-    grpc_exec_ctx_sched(&exec_ctx, callback_list[0], GRPC_ERROR_NONE, NULL);
-    callback_list[0] = NULL;
+  gpr_mu_lock(&s->mu);
+  for (struct op_and_state *curr = s->storage.head; curr != NULL;) {
+    CRONET_LOG(GPR_DEBUG, "calling op at %p. done = %d", curr, curr->done);
+    GPR_ASSERT(curr->done == 0);
+    enum e_op_result result = execute_stream_op(&exec_ctx, curr);
+    CRONET_LOG(GPR_DEBUG, "execute_stream_op[%p] returns %s", curr,
+               op_result_string(result));
+    /* if this op is done, then remove it and free memory */
+    if (curr->done) {
+      struct op_and_state *next = curr->next;
+      remove_from_storage(s, curr);
+      curr = next;
+    }
+    /* continue processing the same op if ACTION_TAKEN_WITHOUT_CALLBACK */
+    if (result == NO_ACTION_POSSIBLE) {
+      curr = curr->next;
+    } else if (result == ACTION_TAKEN_WITH_CALLBACK) {
+      break;
+    }
   }
-  if (callback_list[1]) {
-    grpc_exec_ctx_sched(&exec_ctx, callback_list[1], GRPC_ERROR_NONE, NULL);
-    callback_list[1] = NULL;
-  }
+  gpr_mu_unlock(&s->mu);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
-static void on_canceled(cronet_bidirectional_stream *stream) {
-  if (grpc_cronet_trace) {
-    gpr_log(GPR_DEBUG, "on_canceled %p", stream);
-  }
-}
-
+/*
+  Cronet callback
+*/
 static void on_failed(cronet_bidirectional_stream *stream, int net_error) {
-  if (grpc_cronet_trace) {
-    gpr_log(GPR_DEBUG, "on_failed %p, error = %d", stream, net_error);
+  CRONET_LOG(GPR_DEBUG, "on_failed(%p, %d)", stream, net_error);
+  stream_obj *s = (stream_obj *)stream->annotation;
+  gpr_mu_lock(&s->mu);
+  cronet_bidirectional_stream_destroy(s->cbs);
+  s->state.state_callback_received[OP_FAILED] = true;
+  s->cbs = NULL;
+  if (s->header_array.headers) {
+    gpr_free(s->header_array.headers);
+    s->header_array.headers = NULL;
   }
+  if (s->state.ws.write_buffer) {
+    gpr_free(s->state.ws.write_buffer);
+    s->state.ws.write_buffer = NULL;
+  }
+  gpr_mu_unlock(&s->mu);
+  execute_from_storage(s);
 }
 
+/*
+  Cronet callback
+*/
+static void on_canceled(cronet_bidirectional_stream *stream) {
+  CRONET_LOG(GPR_DEBUG, "on_canceled(%p)", stream);
+  stream_obj *s = (stream_obj *)stream->annotation;
+  gpr_mu_lock(&s->mu);
+  cronet_bidirectional_stream_destroy(s->cbs);
+  s->state.state_callback_received[OP_CANCELED] = true;
+  s->cbs = NULL;
+  if (s->header_array.headers) {
+    gpr_free(s->header_array.headers);
+    s->header_array.headers = NULL;
+  }
+  if (s->state.ws.write_buffer) {
+    gpr_free(s->state.ws.write_buffer);
+    s->state.ws.write_buffer = NULL;
+  }
+  gpr_mu_unlock(&s->mu);
+  execute_from_storage(s);
+}
+
+/*
+  Cronet callback
+*/
 static void on_succeeded(cronet_bidirectional_stream *stream) {
-  if (grpc_cronet_trace) {
-    gpr_log(GPR_DEBUG, "on_succeeded %p", stream);
+  CRONET_LOG(GPR_DEBUG, "on_succeeded(%p)", stream);
+  stream_obj *s = (stream_obj *)stream->annotation;
+  gpr_mu_lock(&s->mu);
+  cronet_bidirectional_stream_destroy(s->cbs);
+  s->state.state_callback_received[OP_SUCCEEDED] = true;
+  s->cbs = NULL;
+  gpr_mu_unlock(&s->mu);
+  execute_from_storage(s);
+}
+
+/*
+  Cronet callback
+*/
+static void on_request_headers_sent(cronet_bidirectional_stream *stream) {
+  CRONET_LOG(GPR_DEBUG, "W: on_request_headers_sent(%p)", stream);
+  stream_obj *s = (stream_obj *)stream->annotation;
+  gpr_mu_lock(&s->mu);
+  s->state.state_op_done[OP_SEND_INITIAL_METADATA] = true;
+  s->state.state_callback_received[OP_SEND_INITIAL_METADATA] = true;
+  /* Free the memory allocated for headers */
+  if (s->header_array.headers) {
+    gpr_free(s->header_array.headers);
+    s->header_array.headers = NULL;
+  }
+  gpr_mu_unlock(&s->mu);
+  execute_from_storage(s);
+}
+
+/*
+  Cronet callback
+*/
+static void on_response_headers_received(
+    cronet_bidirectional_stream *stream,
+    const cronet_bidirectional_stream_header_array *headers,
+    const char *negotiated_protocol) {
+  CRONET_LOG(GPR_DEBUG, "R: on_response_headers_received(%p, %p, %s)", stream,
+             headers, negotiated_protocol);
+  stream_obj *s = (stream_obj *)stream->annotation;
+  gpr_mu_lock(&s->mu);
+  memset(&s->state.rs.initial_metadata, 0,
+         sizeof(s->state.rs.initial_metadata));
+  grpc_chttp2_incoming_metadata_buffer_init(&s->state.rs.initial_metadata);
+  for (size_t i = 0; i < headers->count; i++) {
+    grpc_chttp2_incoming_metadata_buffer_add(
+        &s->state.rs.initial_metadata,
+        grpc_mdelem_from_metadata_strings(
+            grpc_mdstr_from_string(headers->headers[i].key),
+            grpc_mdstr_from_string(headers->headers[i].value)));
+  }
+  s->state.state_callback_received[OP_RECV_INITIAL_METADATA] = true;
+  gpr_mu_unlock(&s->mu);
+  execute_from_storage(s);
+}
+
+/*
+  Cronet callback
+*/
+static void on_write_completed(cronet_bidirectional_stream *stream,
+                               const char *data) {
+  stream_obj *s = (stream_obj *)stream->annotation;
+  CRONET_LOG(GPR_DEBUG, "W: on_write_completed(%p, %s)", stream, data);
+  gpr_mu_lock(&s->mu);
+  if (s->state.ws.write_buffer) {
+    gpr_free(s->state.ws.write_buffer);
+    s->state.ws.write_buffer = NULL;
+  }
+  s->state.state_callback_received[OP_SEND_MESSAGE] = true;
+  gpr_mu_unlock(&s->mu);
+  execute_from_storage(s);
+}
+
+/*
+  Cronet callback
+*/
+static void on_read_completed(cronet_bidirectional_stream *stream, char *data,
+                              int count) {
+  stream_obj *s = (stream_obj *)stream->annotation;
+  CRONET_LOG(GPR_DEBUG, "R: on_read_completed(%p, %p, %d)", stream, data,
+             count);
+  gpr_mu_lock(&s->mu);
+  s->state.state_callback_received[OP_RECV_MESSAGE] = true;
+  if (count > 0) {
+    s->state.rs.received_bytes += count;
+    s->state.rs.remaining_bytes -= count;
+    if (s->state.rs.remaining_bytes > 0) {
+      CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_read(%p)", s->cbs);
+      s->state.state_op_done[OP_READ_REQ_MADE] = true;
+      cronet_bidirectional_stream_read(
+          s->cbs, s->state.rs.read_buffer + s->state.rs.received_bytes,
+          s->state.rs.remaining_bytes);
+      gpr_mu_unlock(&s->mu);
+    } else {
+      gpr_mu_unlock(&s->mu);
+      execute_from_storage(s);
+    }
+  } else {
+    s->state.rs.read_stream_closed = true;
+    gpr_mu_unlock(&s->mu);
+    execute_from_storage(s);
   }
 }
 
+/*
+  Cronet callback
+*/
 static void on_response_trailers_received(
     cronet_bidirectional_stream *stream,
     const cronet_bidirectional_stream_header_array *trailers) {
-  if (grpc_cronet_trace) {
-    gpr_log(GPR_DEBUG, "R: on_response_trailers_received");
-  }
+  CRONET_LOG(GPR_DEBUG, "R: on_response_trailers_received(%p,%p)", stream,
+             trailers);
   stream_obj *s = (stream_obj *)stream->annotation;
-
-  memset(&s->imb, 0, sizeof(s->imb));
-  grpc_chttp2_incoming_metadata_buffer_init(&s->imb);
-  unsigned int i = 0;
-  for (i = 0; i < trailers->count; i++) {
+  gpr_mu_lock(&s->mu);
+  memset(&s->state.rs.trailing_metadata, 0,
+         sizeof(s->state.rs.trailing_metadata));
+  s->state.rs.trailing_metadata_valid = false;
+  grpc_chttp2_incoming_metadata_buffer_init(&s->state.rs.trailing_metadata);
+  for (size_t i = 0; i < trailers->count; i++) {
+    CRONET_LOG(GPR_DEBUG, "trailer key=%s, value=%s", trailers->headers[i].key,
+               trailers->headers[i].value);
     grpc_chttp2_incoming_metadata_buffer_add(
-        &s->imb, grpc_mdelem_from_metadata_strings(
-                     grpc_mdstr_from_string(trailers->headers[i].key),
-                     grpc_mdstr_from_string(trailers->headers[i].value)));
+        &s->state.rs.trailing_metadata,
+        grpc_mdelem_from_metadata_strings(
+            grpc_mdstr_from_string(trailers->headers[i].key),
+            grpc_mdstr_from_string(trailers->headers[i].value)));
+    s->state.rs.trailing_metadata_valid = true;
   }
-  s->response_trailers_received = true;
-  next_recv_step(s, ON_RESPONSE_TRAILERS_RECEIVED);
+  s->state.state_callback_received[OP_RECV_TRAILING_METADATA] = true;
+  gpr_mu_unlock(&s->mu);
+  execute_from_storage(s);
 }
 
-static void on_write_completed(cronet_bidirectional_stream *stream,
-                               const char *data) {
-  if (grpc_cronet_trace) {
-    gpr_log(GPR_DEBUG, "W: on_write_completed");
-  }
-  stream_obj *s = (stream_obj *)stream->annotation;
-  enqueue_callbacks(s->callback_list[CB_SEND_MESSAGE]);
-  s->cronet_send_state = CRONET_WRITE_COMPLETED;
-  next_send_step(s);
+/*
+ Utility function that takes the data from s->write_slice_buffer and assembles
+ into a contiguous byte stream with 5 byte gRPC header prepended.
+*/
+static void create_grpc_frame(gpr_slice_buffer *write_slice_buffer,
+                              char **pp_write_buffer,
+                              size_t *p_write_buffer_size) {
+  gpr_slice slice = gpr_slice_buffer_take_first(write_slice_buffer);
+  size_t length = GPR_SLICE_LENGTH(slice);
+  *p_write_buffer_size = length + GRPC_HEADER_SIZE_IN_BYTES;
+  /* This is freed in the on_write_completed callback */
+  char *write_buffer = gpr_malloc(length + GRPC_HEADER_SIZE_IN_BYTES);
+  *pp_write_buffer = write_buffer;
+  uint8_t *p = (uint8_t *)write_buffer;
+  /* Append 5 byte header */
+  *p++ = 0;
+  *p++ = (uint8_t)(length >> 24);
+  *p++ = (uint8_t)(length >> 16);
+  *p++ = (uint8_t)(length >> 8);
+  *p++ = (uint8_t)(length);
+  /* append actual data */
+  memcpy(p, GPR_SLICE_START_PTR(slice), length);
 }
 
-static void process_recv_message(stream_obj *s, const uint8_t *recv_data) {
-  gpr_slice read_data_slice = gpr_slice_malloc((uint32_t)s->total_read_bytes);
-  uint8_t *dst_p = GPR_SLICE_START_PTR(read_data_slice);
-  if (s->total_read_bytes > 0) {
-    // Only copy if there is non-zero number of bytes
-    memcpy(dst_p, recv_data, (size_t)s->total_read_bytes);
-    gpr_slice_buffer_add(&s->read_slice_buffer, read_data_slice);
+/*
+ Convert metadata in a format that Cronet can consume
+*/
+static void convert_metadata_to_cronet_headers(
+    grpc_linked_mdelem *head, const char *host, char **pp_url,
+    cronet_bidirectional_stream_header **pp_headers, size_t *p_num_headers) {
+  grpc_linked_mdelem *curr = head;
+  /* Walk the linked list and get number of header fields */
+  size_t num_headers_available = 0;
+  while (curr != NULL) {
+    curr = curr->next;
+    num_headers_available++;
   }
-  grpc_slice_buffer_stream_init(&s->sbs, &s->read_slice_buffer, 0);
-  *s->recv_message = (grpc_byte_buffer *)&s->sbs;
+  /* Allocate enough memory. It is freed in the on_request_headers_sent callback
+   */
+  cronet_bidirectional_stream_header *headers =
+      (cronet_bidirectional_stream_header *)gpr_malloc(
+          sizeof(cronet_bidirectional_stream_header) * num_headers_available);
+  *pp_headers = headers;
+
+  /* Walk the linked list again, this time copying the header fields.
+    s->num_headers can be less than num_headers_available, as some headers
+    are not used for cronet.
+    TODO (makdharma): Eliminate need to traverse the LL second time for perf.
+   */
+  curr = head;
+  size_t num_headers = 0;
+  while (num_headers < num_headers_available) {
+    grpc_mdelem *mdelem = curr->md;
+    curr = curr->next;
+    const char *key = grpc_mdstr_as_c_string(mdelem->key);
+    const char *value = grpc_mdstr_as_c_string(mdelem->value);
+    if (mdelem->key == GRPC_MDSTR_METHOD || mdelem->key == GRPC_MDSTR_SCHEME ||
+        mdelem->key == GRPC_MDSTR_AUTHORITY) {
+      /* Cronet populates these fields on its own */
+      continue;
+    }
+    if (mdelem->key == GRPC_MDSTR_PATH) {
+      /* Create URL by appending :path value to the hostname */
+      gpr_asprintf(pp_url, "https://%s%s", host, value);
+      continue;
+    }
+    CRONET_LOG(GPR_DEBUG, "header %s = %s", key, value);
+    headers[num_headers].key = key;
+    headers[num_headers].value = value;
+    num_headers++;
+    if (curr == NULL) {
+      break;
+    }
+  }
+  *p_num_headers = (size_t)num_headers;
 }
 
 static int parse_grpc_header(const uint8_t *data) {
@@ -241,422 +589,458 @@
   return length;
 }
 
-static void on_read_completed(cronet_bidirectional_stream *stream, char *data,
-                              int count) {
-  stream_obj *s = (stream_obj *)stream->annotation;
-  if (grpc_cronet_trace) {
-    gpr_log(GPR_DEBUG, "R: on_read_completed count=%d, total=%d, remaining=%d",
-            count, s->total_read_bytes, s->remaining_read_bytes);
-  }
-  if (count > 0) {
-    GPR_ASSERT(s->recv_message);
-    s->remaining_read_bytes -= count;
-    next_recv_step(s, ON_READ_COMPLETE);
-  } else {
-    s->read_closed = true;
-    next_recv_step(s, ON_READ_COMPLETE);
-  }
-}
-
-static void on_response_headers_received(
-    cronet_bidirectional_stream *stream,
-    const cronet_bidirectional_stream_header_array *headers,
-    const char *negotiated_protocol) {
-  if (grpc_cronet_trace) {
-    gpr_log(GPR_DEBUG, "R: on_response_headers_received");
-  }
-  stream_obj *s = (stream_obj *)stream->annotation;
-  enqueue_callbacks(s->callback_list[CB_RECV_INITIAL_METADATA]);
-  s->response_headers_received = true;
-  next_recv_step(s, ON_RESPONSE_HEADERS_RECEIVED);
-}
-
-static void on_request_headers_sent(cronet_bidirectional_stream *stream) {
-  if (grpc_cronet_trace) {
-    gpr_log(GPR_DEBUG, "W: on_request_headers_sent");
-  }
-  stream_obj *s = (stream_obj *)stream->annotation;
-  enqueue_callbacks(s->callback_list[CB_SEND_INITIAL_METADATA]);
-  s->cronet_send_state = CRONET_SEND_HEADER;
-  next_send_step(s);
-}
-
-// Callback function pointers (invoked by cronet in response to events)
-static cronet_bidirectional_stream_callback callbacks = {
-    on_request_headers_sent,
-    on_response_headers_received,
-    on_read_completed,
-    on_write_completed,
-    on_response_trailers_received,
-    on_succeeded,
-    on_failed,
-    on_canceled};
-
-static void invoke_closing_callback(stream_obj *s) {
-  grpc_chttp2_incoming_metadata_buffer_publish(&s->imb,
-                                               s->recv_trailing_metadata);
-  if (s->callback_list[CB_RECV_TRAILING_METADATA]) {
-    enqueue_callbacks(s->callback_list[CB_RECV_TRAILING_METADATA]);
-  }
-}
-
-static void set_recv_state(stream_obj *s, enum recv_state state) {
-  if (grpc_cronet_trace) {
-    gpr_log(GPR_DEBUG, "next_state = %s", recv_state_name[state]);
-  }
-  s->cronet_recv_state = state;
-}
-
-// This is invoked from perform_stream_op, and all on_xxxx callbacks.
-static void next_recv_step(stream_obj *s, enum e_caller caller) {
-  gpr_mu_lock(&s->recv_mu);
-  switch (s->cronet_recv_state) {
-    case CRONET_RECV_IDLE:
-      if (grpc_cronet_trace) {
-        gpr_log(GPR_DEBUG, "cronet_recv_state = CRONET_RECV_IDLE");
+/*
+  Op Execution: Decide if one of the actions contained in the stream op can be
+  executed. This is the heart of the state machine.
+*/
+static bool op_can_be_run(grpc_transport_stream_op *curr_op,
+                          struct op_state *stream_state,
+                          struct op_state *op_state, enum e_op_id op_id) {
+  bool result = true;
+  /* When call is canceled, every op can be run, except under following
+  conditions
+  */
+  bool is_canceled_of_failed = stream_state->state_op_done[OP_CANCEL_ERROR] ||
+                               stream_state->state_callback_received[OP_FAILED];
+  if (is_canceled_of_failed) {
+    if (op_id == OP_SEND_INITIAL_METADATA) result = false;
+    if (op_id == OP_SEND_MESSAGE) result = false;
+    if (op_id == OP_SEND_TRAILING_METADATA) result = false;
+    if (op_id == OP_CANCEL_ERROR) result = false;
+    /* already executed */
+    if (op_id == OP_RECV_INITIAL_METADATA &&
+        stream_state->state_op_done[OP_RECV_INITIAL_METADATA])
+      result = false;
+    if (op_id == OP_RECV_MESSAGE &&
+        stream_state->state_op_done[OP_RECV_MESSAGE])
+      result = false;
+    if (op_id == OP_RECV_TRAILING_METADATA &&
+        stream_state->state_op_done[OP_RECV_TRAILING_METADATA])
+      result = false;
+  } else if (op_id == OP_SEND_INITIAL_METADATA) {
+    /* already executed */
+    if (stream_state->state_op_done[OP_SEND_INITIAL_METADATA]) result = false;
+  } else if (op_id == OP_RECV_INITIAL_METADATA) {
+    /* already executed */
+    if (stream_state->state_op_done[OP_RECV_INITIAL_METADATA]) result = false;
+    /* we haven't sent headers yet. */
+    else if (!stream_state->state_callback_received[OP_SEND_INITIAL_METADATA])
+      result = false;
+    /* we haven't received headers yet. */
+    else if (!stream_state->state_callback_received[OP_RECV_INITIAL_METADATA])
+      result = false;
+  } else if (op_id == OP_SEND_MESSAGE) {
+    /* already executed (note we're checking op specific state, not stream
+     state) */
+    if (op_state->state_op_done[OP_SEND_MESSAGE]) result = false;
+    /* we haven't sent headers yet. */
+    else if (!stream_state->state_callback_received[OP_SEND_INITIAL_METADATA])
+      result = false;
+  } else if (op_id == OP_RECV_MESSAGE) {
+    /* already executed */
+    if (op_state->state_op_done[OP_RECV_MESSAGE]) result = false;
+    /* we haven't received headers yet. */
+    else if (!stream_state->state_callback_received[OP_RECV_INITIAL_METADATA])
+      result = false;
+  } else if (op_id == OP_RECV_TRAILING_METADATA) {
+    /* already executed */
+    if (stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) result = false;
+    /* we have asked for but haven't received message yet. */
+    else if (stream_state->state_op_done[OP_READ_REQ_MADE] &&
+             !stream_state->state_op_done[OP_RECV_MESSAGE])
+      result = false;
+    /* we haven't received trailers  yet. */
+    else if (!stream_state->state_callback_received[OP_RECV_TRAILING_METADATA])
+      result = false;
+    /* we haven't received on_succeeded  yet. */
+    else if (!stream_state->state_callback_received[OP_SUCCEEDED])
+      result = false;
+  } else if (op_id == OP_SEND_TRAILING_METADATA) {
+    /* already executed */
+    if (stream_state->state_op_done[OP_SEND_TRAILING_METADATA]) result = false;
+    /* we haven't sent initial metadata yet */
+    else if (!stream_state->state_callback_received[OP_SEND_INITIAL_METADATA])
+      result = false;
+    /* we haven't sent message yet */
+    else if (curr_op->send_message &&
+             !stream_state->state_op_done[OP_SEND_MESSAGE])
+      result = false;
+    /* we haven't got on_write_completed for the send yet */
+    else if (stream_state->state_op_done[OP_SEND_MESSAGE] &&
+             !stream_state->state_callback_received[OP_SEND_MESSAGE])
+      result = false;
+  } else if (op_id == OP_CANCEL_ERROR) {
+    /* already executed */
+    if (stream_state->state_op_done[OP_CANCEL_ERROR]) result = false;
+  } else if (op_id == OP_ON_COMPLETE) {
+    /* already executed (note we're checking op specific state, not stream
+    state) */
+    if (op_state->state_op_done[OP_ON_COMPLETE]) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    }
+    /* Check if every op that was asked for is done. */
+    else if (curr_op->send_initial_metadata &&
+             !stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    } else if (curr_op->send_message &&
+               !op_state->state_op_done[OP_SEND_MESSAGE]) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    } else if (curr_op->send_message &&
+               !stream_state->state_callback_received[OP_SEND_MESSAGE]) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    } else if (curr_op->send_trailing_metadata &&
+               !stream_state->state_op_done[OP_SEND_TRAILING_METADATA]) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    } else if (curr_op->recv_initial_metadata &&
+               !stream_state->state_op_done[OP_RECV_INITIAL_METADATA]) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    } else if (curr_op->recv_message &&
+               !stream_state->state_op_done[OP_RECV_MESSAGE]) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    } else if (curr_op->recv_trailing_metadata) {
+      /* We aren't done with trailing metadata yet */
+      if (!stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) {
+        CRONET_LOG(GPR_DEBUG, "Because");
+        result = false;
       }
-      if (caller == PERFORM_STREAM_OP ||
-          caller == ON_RESPONSE_HEADERS_RECEIVED) {
-        if (s->read_closed && s->response_trailers_received) {
-          invoke_closing_callback(s);
-          set_recv_state(s, CRONET_RECV_CLOSED);
-        } else if (s->response_headers_received == true &&
-                   s->read_requested == true) {
-          set_recv_state(s, CRONET_RECV_READ_LENGTH);
-          s->total_read_bytes = s->remaining_read_bytes =
-              GRPC_HEADER_SIZE_IN_BYTES;
-          GPR_ASSERT(s->read_buffer);
-          if (grpc_cronet_trace) {
-            gpr_log(GPR_DEBUG, "R: cronet_bidirectional_stream_read()");
-          }
-          cronet_bidirectional_stream_read(s->cbs, s->read_buffer,
-                                           s->remaining_read_bytes);
+      /* We've asked for actual message in an earlier op, and it hasn't been
+        delivered yet. */
+      else if (stream_state->state_op_done[OP_READ_REQ_MADE]) {
+        /* If this op is not the one asking for read, (which means some earlier
+          op has asked), and the read hasn't been delivered. */
+        if (!curr_op->recv_message &&
+            !stream_state->state_callback_received[OP_SUCCEEDED]) {
+          CRONET_LOG(GPR_DEBUG, "Because");
+          result = false;
         }
       }
-      break;
-    case CRONET_RECV_READ_LENGTH:
-      if (grpc_cronet_trace) {
-        gpr_log(GPR_DEBUG, "cronet_recv_state = CRONET_RECV_READ_LENGTH");
-      }
-      if (caller == ON_READ_COMPLETE) {
-        if (s->read_closed) {
-          invoke_closing_callback(s);
-          enqueue_callbacks(s->callback_list[CB_RECV_MESSAGE]);
-          set_recv_state(s, CRONET_RECV_CLOSED);
-        } else {
-          GPR_ASSERT(s->remaining_read_bytes == 0);
-          set_recv_state(s, CRONET_RECV_READ_DATA);
-          s->total_read_bytes = s->remaining_read_bytes =
-              parse_grpc_header((const uint8_t *)s->read_buffer);
-          s->read_buffer =
-              gpr_realloc(s->read_buffer, (uint32_t)s->remaining_read_bytes);
-          GPR_ASSERT(s->read_buffer);
-          if (grpc_cronet_trace) {
-            gpr_log(GPR_DEBUG, "R: cronet_bidirectional_stream_read()");
-          }
-          if (s->remaining_read_bytes > 0) {
-            cronet_bidirectional_stream_read(s->cbs, (char *)s->read_buffer,
-                                             s->remaining_read_bytes);
-          } else {
-            // Calling the closing callback directly since this is a 0 byte read
-            // for an empty message.
-            process_recv_message(s, NULL);
-            enqueue_callbacks(s->callback_list[CB_RECV_MESSAGE]);
-            invoke_closing_callback(s);
-            set_recv_state(s, CRONET_RECV_CLOSED);
-          }
-        }
-      }
-      break;
-    case CRONET_RECV_READ_DATA:
-      if (grpc_cronet_trace) {
-        gpr_log(GPR_DEBUG, "cronet_recv_state = CRONET_RECV_READ_DATA");
-      }
-      if (caller == ON_READ_COMPLETE) {
-        if (s->remaining_read_bytes > 0) {
-          int offset = s->total_read_bytes - s->remaining_read_bytes;
-          GPR_ASSERT(s->read_buffer);
-          if (grpc_cronet_trace) {
-            gpr_log(GPR_DEBUG, "R: cronet_bidirectional_stream_read()");
-          }
-          cronet_bidirectional_stream_read(
-              s->cbs, (char *)s->read_buffer + offset, s->remaining_read_bytes);
-        } else {
-          gpr_slice_buffer_init(&s->read_slice_buffer);
-          uint8_t *p = (uint8_t *)s->read_buffer;
-          process_recv_message(s, p);
-          set_recv_state(s, CRONET_RECV_IDLE);
-          enqueue_callbacks(s->callback_list[CB_RECV_MESSAGE]);
-        }
-      }
-      break;
-    case CRONET_RECV_CLOSED:
-      break;
-    default:
-      GPR_ASSERT(0);  // Should not reach here
-      break;
+    }
+    /* We should see at least one on_write_completed for the trailers that we
+      sent */
+    else if (curr_op->send_trailing_metadata &&
+             !stream_state->state_callback_received[OP_SEND_MESSAGE])
+      result = false;
   }
-  gpr_mu_unlock(&s->recv_mu);
+  CRONET_LOG(GPR_DEBUG, "op_can_be_run %s : %s", op_id_string(op_id),
+             result ? "YES" : "NO");
+  return result;
 }
 
-// This function takes the data from s->write_slice_buffer and assembles into
-// a contiguous byte stream with 5 byte gRPC header prepended.
-static void create_grpc_frame(stream_obj *s) {
-  gpr_slice slice = gpr_slice_buffer_take_first(&s->write_slice_buffer);
-  uint8_t *raw_data = GPR_SLICE_START_PTR(slice);
-  size_t length = GPR_SLICE_LENGTH(slice);
-  s->write_buffer_size = length + GRPC_HEADER_SIZE_IN_BYTES;
-  s->write_buffer = gpr_realloc(s->write_buffer, s->write_buffer_size);
-  uint8_t *p = (uint8_t *)s->write_buffer;
-  // Append 5 byte header
-  *p++ = 0;
-  *p++ = (uint8_t)(length >> 24);
-  *p++ = (uint8_t)(length >> 16);
-  *p++ = (uint8_t)(length >> 8);
-  *p++ = (uint8_t)(length);
-  // append actual data
-  memcpy(p, raw_data, length);
-}
-
-static void do_write(stream_obj *s) {
-  gpr_slice_buffer *sb = &s->write_slice_buffer;
-  GPR_ASSERT(sb->count <= 1);
-  if (sb->count > 0) {
-    create_grpc_frame(s);
-    if (grpc_cronet_trace) {
-      gpr_log(GPR_DEBUG, "W: cronet_bidirectional_stream_write");
-    }
-    cronet_bidirectional_stream_write(s->cbs, s->write_buffer,
-                                      (int)s->write_buffer_size, false);
-  }
-}
-
-//
-static void next_send_step(stream_obj *s) {
-  switch (s->cronet_send_state) {
-    case CRONET_SEND_IDLE:
-      GPR_ASSERT(
-          s->cbs);  // cronet_bidirectional_stream is not initialized yet.
-      s->cronet_send_state = CRONET_REQ_STARTED;
-      if (grpc_cronet_trace) {
-        gpr_log(GPR_DEBUG, "cronet_bidirectional_stream_start to %s", s->url);
-      }
-      cronet_bidirectional_stream_start(s->cbs, s->url, 0, "POST",
-                                        &s->header_array, false);
-      // we no longer need the memory that was allocated earlier.
-      gpr_free(s->header_array.headers);
-      break;
-    case CRONET_SEND_HEADER:
-      do_write(s);
-      s->cronet_send_state = CRONET_WRITE;
-      break;
-    case CRONET_WRITE_COMPLETED:
-      do_write(s);
-      break;
-    default:
-      GPR_ASSERT(0);
-      break;
-  }
-}
-
-static void convert_metadata_to_cronet_headers(grpc_linked_mdelem *head,
-                                               const char *host,
-                                               stream_obj *s) {
-  grpc_linked_mdelem *curr = head;
-  // Walk the linked list and get number of header fields
-  uint32_t num_headers_available = 0;
-  while (curr != NULL) {
-    curr = curr->next;
-    num_headers_available++;
-  }
-  // Allocate enough memory
-  s->headers = (cronet_bidirectional_stream_header *)gpr_malloc(
-      sizeof(cronet_bidirectional_stream_header) * num_headers_available);
-
-  // Walk the linked list again, this time copying the header fields.
-  // s->num_headers
-  // can be less than num_headers_available, as some headers are not used for
-  // cronet
-  curr = head;
-  s->num_headers = 0;
-  while (s->num_headers < num_headers_available) {
-    grpc_mdelem *mdelem = curr->md;
-    curr = curr->next;
-    const char *key = grpc_mdstr_as_c_string(mdelem->key);
-    const char *value = grpc_mdstr_as_c_string(mdelem->value);
-    if (strcmp(key, ":scheme") == 0 || strcmp(key, ":method") == 0 ||
-        strcmp(key, ":authority") == 0) {
-      // Cronet populates these fields on its own.
-      continue;
-    }
-    if (strcmp(key, ":path") == 0) {
-      // Create URL by appending :path value to the hostname
-      gpr_asprintf(&s->url, "https://%s%s", host, value);
-      if (grpc_cronet_trace) {
-        gpr_log(GPR_DEBUG, "extracted URL = %s", s->url);
-      }
-      continue;
-    }
-    s->headers[s->num_headers].key = key;
-    s->headers[s->num_headers].value = value;
-    s->num_headers++;
-    if (curr == NULL) {
-      break;
-    }
-  }
-}
-
-static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
-                              grpc_stream *gs, grpc_transport_stream_op *op) {
-  grpc_cronet_transport *ct = (grpc_cronet_transport *)gt;
-  GPR_ASSERT(ct->engine);
-  stream_obj *s = (stream_obj *)gs;
-  if (op->recv_trailing_metadata) {
-    if (grpc_cronet_trace) {
-      gpr_log(GPR_DEBUG,
-              "perform_stream_op - recv_trailing_metadata: on_complete=%p",
-              op->on_complete);
-    }
-    s->recv_trailing_metadata = op->recv_trailing_metadata;
-    GPR_ASSERT(!s->callback_list[CB_RECV_TRAILING_METADATA][0]);
-    s->callback_list[CB_RECV_TRAILING_METADATA][0] = op->on_complete;
-  }
-  if (op->recv_message) {
-    if (grpc_cronet_trace) {
-      gpr_log(GPR_DEBUG, "perform_stream_op - recv_message: on_complete=%p",
-              op->on_complete);
-    }
-    s->recv_message = (grpc_byte_buffer **)op->recv_message;
-    GPR_ASSERT(!s->callback_list[CB_RECV_MESSAGE][0]);
-    GPR_ASSERT(!s->callback_list[CB_RECV_MESSAGE][1]);
-    s->callback_list[CB_RECV_MESSAGE][0] = op->recv_message_ready;
-    s->callback_list[CB_RECV_MESSAGE][1] = op->on_complete;
-    s->read_requested = true;
-    next_recv_step(s, PERFORM_STREAM_OP);
-  }
-  if (op->recv_initial_metadata) {
-    if (grpc_cronet_trace) {
-      gpr_log(GPR_DEBUG, "perform_stream_op - recv_initial_metadata:=%p",
-              op->on_complete);
-    }
-    s->recv_initial_metadata = op->recv_initial_metadata;
-    GPR_ASSERT(!s->callback_list[CB_RECV_INITIAL_METADATA][0]);
-    GPR_ASSERT(!s->callback_list[CB_RECV_INITIAL_METADATA][1]);
-    s->callback_list[CB_RECV_INITIAL_METADATA][0] =
-        op->recv_initial_metadata_ready;
-    s->callback_list[CB_RECV_INITIAL_METADATA][1] = op->on_complete;
-  }
-  if (op->send_initial_metadata) {
-    if (grpc_cronet_trace) {
-      gpr_log(GPR_DEBUG,
-              "perform_stream_op - send_initial_metadata: on_complete=%p",
-              op->on_complete);
-    }
-    s->num_headers = 0;
-    convert_metadata_to_cronet_headers(op->send_initial_metadata->list.head,
-                                       ct->host, s);
-    s->header_array.count = s->num_headers;
-    s->header_array.capacity = s->num_headers;
-    s->header_array.headers = s->headers;
-    GPR_ASSERT(!s->callback_list[CB_SEND_INITIAL_METADATA][0]);
-    s->callback_list[CB_SEND_INITIAL_METADATA][0] = op->on_complete;
-  }
-  if (op->send_message) {
-    if (grpc_cronet_trace) {
-      gpr_log(GPR_DEBUG, "perform_stream_op - send_message: on_complete=%p",
-              op->on_complete);
-    }
-    grpc_byte_stream_next(exec_ctx, op->send_message, &s->slice,
-                          op->send_message->length, NULL);
-    // Check that compression flag is not ON. We don't support compression yet.
-    // TODO (makdharma): add compression support
-    GPR_ASSERT(op->send_message->flags == 0);
-    gpr_slice_buffer_add(&s->write_slice_buffer, s->slice);
-    if (s->cbs == NULL) {
-      if (grpc_cronet_trace) {
-        gpr_log(GPR_DEBUG, "cronet_bidirectional_stream_create");
-      }
-      s->cbs = cronet_bidirectional_stream_create(ct->engine, s, &callbacks);
-      GPR_ASSERT(s->cbs);
-      s->read_closed = false;
-      s->response_trailers_received = false;
-      s->response_headers_received = false;
-      s->cronet_send_state = CRONET_SEND_IDLE;
-      s->cronet_recv_state = CRONET_RECV_IDLE;
-    }
-    GPR_ASSERT(!s->callback_list[CB_SEND_MESSAGE][0]);
-    s->callback_list[CB_SEND_MESSAGE][0] = op->on_complete;
-    next_send_step(s);
-  }
-  if (op->send_trailing_metadata) {
-    if (grpc_cronet_trace) {
-      gpr_log(GPR_DEBUG,
-              "perform_stream_op - send_trailing_metadata: on_complete=%p",
-              op->on_complete);
-    }
-    GPR_ASSERT(!s->callback_list[CB_SEND_TRAILING_METADATA][0]);
-    s->callback_list[CB_SEND_TRAILING_METADATA][0] = op->on_complete;
-    if (s->cbs) {
-      // Send an "empty" write to the far end to signal that we're done.
-      // This will induce the server to send down trailers.
-      if (grpc_cronet_trace) {
-        gpr_log(GPR_DEBUG, "W: cronet_bidirectional_stream_write");
-      }
-      cronet_bidirectional_stream_write(s->cbs, "abc", 0, true);
+/*
+  TODO (makdharma): Break down this function in smaller chunks for readability.
+*/
+static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
+                                          struct op_and_state *oas) {
+  grpc_transport_stream_op *stream_op = &oas->op;
+  struct stream_obj *s = oas->s;
+  struct op_state *stream_state = &s->state;
+  enum e_op_result result = NO_ACTION_POSSIBLE;
+  if (stream_op->send_initial_metadata &&
+      op_can_be_run(stream_op, stream_state, &oas->state,
+                    OP_SEND_INITIAL_METADATA)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p OP_SEND_INITIAL_METADATA", oas);
+    /* This OP is the beginning. Reset various states */
+    memset(&s->header_array, 0, sizeof(s->header_array));
+    memset(&stream_state->rs, 0, sizeof(stream_state->rs));
+    memset(&stream_state->ws, 0, sizeof(stream_state->ws));
+    memset(stream_state->state_op_done, 0, sizeof(stream_state->state_op_done));
+    memset(stream_state->state_callback_received, 0,
+           sizeof(stream_state->state_callback_received));
+    /* Start new cronet stream. It is destroyed in on_succeeded, on_canceled,
+     * on_failed */
+    GPR_ASSERT(s->cbs == NULL);
+    s->cbs = cronet_bidirectional_stream_create(s->curr_ct.engine, s->curr_gs,
+                                                &cronet_callbacks);
+    CRONET_LOG(GPR_DEBUG, "%p = cronet_bidirectional_stream_create()", s->cbs);
+    char *url;
+    s->header_array.headers = NULL;
+    convert_metadata_to_cronet_headers(
+        stream_op->send_initial_metadata->list.head, s->curr_ct.host, &url,
+        &s->header_array.headers, &s->header_array.count);
+    s->header_array.capacity = s->header_array.count;
+    CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_start(%p, %s)", s->cbs,
+               url);
+    cronet_bidirectional_stream_start(s->cbs, url, 0, "POST", &s->header_array,
+                                      false);
+    stream_state->state_op_done[OP_SEND_INITIAL_METADATA] = true;
+    result = ACTION_TAKEN_WITH_CALLBACK;
+  } else if (stream_op->recv_initial_metadata &&
+             op_can_be_run(stream_op, stream_state, &oas->state,
+                           OP_RECV_INITIAL_METADATA)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p  OP_RECV_INITIAL_METADATA", oas);
+    if (stream_state->state_op_done[OP_CANCEL_ERROR] ||
+        stream_state->state_callback_received[OP_FAILED]) {
+      grpc_exec_ctx_sched(exec_ctx, stream_op->recv_initial_metadata_ready,
+                          GRPC_ERROR_CANCELLED, NULL);
     } else {
-      // We never created a stream. This was probably an empty request.
-      invoke_closing_callback(s);
+      grpc_chttp2_incoming_metadata_buffer_publish(
+          &oas->s->state.rs.initial_metadata, stream_op->recv_initial_metadata);
+      grpc_exec_ctx_sched(exec_ctx, stream_op->recv_initial_metadata_ready,
+                          GRPC_ERROR_NONE, NULL);
     }
+    stream_state->state_op_done[OP_RECV_INITIAL_METADATA] = true;
+    result = ACTION_TAKEN_NO_CALLBACK;
+  } else if (stream_op->send_message &&
+             op_can_be_run(stream_op, stream_state, &oas->state,
+                           OP_SEND_MESSAGE)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p  OP_SEND_MESSAGE", oas);
+    if (stream_state->state_callback_received[OP_FAILED]) {
+      result = NO_ACTION_POSSIBLE;
+      CRONET_LOG(GPR_DEBUG, "Stream is either cancelled or failed.");
+    } else {
+      gpr_slice_buffer write_slice_buffer;
+      gpr_slice slice;
+      gpr_slice_buffer_init(&write_slice_buffer);
+      grpc_byte_stream_next(NULL, stream_op->send_message, &slice,
+                            stream_op->send_message->length, NULL);
+      /* Check that compression flag is OFF. We don't support compression yet.
+       */
+      if (stream_op->send_message->flags != 0) {
+        gpr_log(GPR_ERROR, "Compression is not supported");
+        GPR_ASSERT(stream_op->send_message->flags == 0);
+      }
+      gpr_slice_buffer_add(&write_slice_buffer, slice);
+      if (write_slice_buffer.count != 1) {
+        /* Empty request not handled yet */
+        gpr_log(GPR_ERROR, "Empty request is not supported");
+        GPR_ASSERT(write_slice_buffer.count == 1);
+      }
+      if (write_slice_buffer.count > 0) {
+        size_t write_buffer_size;
+        create_grpc_frame(&write_slice_buffer, &stream_state->ws.write_buffer,
+                          &write_buffer_size);
+        CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_write (%p, %p)",
+                   s->cbs, stream_state->ws.write_buffer);
+        stream_state->state_callback_received[OP_SEND_MESSAGE] = false;
+        cronet_bidirectional_stream_write(s->cbs, stream_state->ws.write_buffer,
+                                          (int)write_buffer_size, false);
+        result = ACTION_TAKEN_WITH_CALLBACK;
+      } else {
+        result = NO_ACTION_POSSIBLE;
+      }
+    }
+    stream_state->state_op_done[OP_SEND_MESSAGE] = true;
+    oas->state.state_op_done[OP_SEND_MESSAGE] = true;
+  } else if (stream_op->recv_message &&
+             op_can_be_run(stream_op, stream_state, &oas->state,
+                           OP_RECV_MESSAGE)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p  OP_RECV_MESSAGE", oas);
+    if (stream_state->state_op_done[OP_CANCEL_ERROR] ||
+        stream_state->state_callback_received[OP_FAILED]) {
+      CRONET_LOG(GPR_DEBUG, "Stream is either cancelled or failed.");
+      grpc_exec_ctx_sched(exec_ctx, stream_op->recv_message_ready,
+                          GRPC_ERROR_CANCELLED, NULL);
+      stream_state->state_op_done[OP_RECV_MESSAGE] = true;
+    } else if (stream_state->rs.read_stream_closed == true) {
+      /* No more data will be received */
+      CRONET_LOG(GPR_DEBUG, "read stream closed");
+      grpc_exec_ctx_sched(exec_ctx, stream_op->recv_message_ready,
+                          GRPC_ERROR_NONE, NULL);
+      stream_state->state_op_done[OP_RECV_MESSAGE] = true;
+      oas->state.state_op_done[OP_RECV_MESSAGE] = true;
+    } else if (stream_state->rs.length_field_received == false) {
+      if (stream_state->rs.received_bytes == GRPC_HEADER_SIZE_IN_BYTES &&
+          stream_state->rs.remaining_bytes == 0) {
+        /* Start a read operation for data */
+        stream_state->rs.length_field_received = true;
+        stream_state->rs.length_field = stream_state->rs.remaining_bytes =
+            parse_grpc_header((const uint8_t *)stream_state->rs.read_buffer);
+        CRONET_LOG(GPR_DEBUG, "length field = %d",
+                   stream_state->rs.length_field);
+        if (stream_state->rs.length_field > 0) {
+          stream_state->rs.read_buffer =
+              gpr_malloc((size_t)stream_state->rs.length_field);
+          GPR_ASSERT(stream_state->rs.read_buffer);
+          stream_state->rs.remaining_bytes = stream_state->rs.length_field;
+          stream_state->rs.received_bytes = 0;
+          CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_read(%p)", s->cbs);
+          stream_state->state_op_done[OP_READ_REQ_MADE] =
+              true; /* Indicates that at least one read request has been made */
+          cronet_bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer,
+                                           stream_state->rs.remaining_bytes);
+          result = ACTION_TAKEN_WITH_CALLBACK;
+        } else {
+          stream_state->rs.remaining_bytes = 0;
+          CRONET_LOG(GPR_DEBUG, "read operation complete. Empty response.");
+          gpr_slice_buffer_init(&stream_state->rs.read_slice_buffer);
+          grpc_slice_buffer_stream_init(&stream_state->rs.sbs,
+                                        &stream_state->rs.read_slice_buffer, 0);
+          *((grpc_byte_buffer **)stream_op->recv_message) =
+              (grpc_byte_buffer *)&stream_state->rs.sbs;
+          grpc_exec_ctx_sched(exec_ctx, stream_op->recv_message_ready,
+                              GRPC_ERROR_NONE, NULL);
+          stream_state->state_op_done[OP_RECV_MESSAGE] = true;
+          oas->state.state_op_done[OP_RECV_MESSAGE] = true;
+          result = ACTION_TAKEN_NO_CALLBACK;
+        }
+      } else if (stream_state->rs.remaining_bytes == 0) {
+        /* Start a read operation for first 5 bytes (GRPC header) */
+        stream_state->rs.read_buffer = stream_state->rs.grpc_header_bytes;
+        stream_state->rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES;
+        stream_state->rs.received_bytes = 0;
+        CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_read(%p)", s->cbs);
+        stream_state->state_op_done[OP_READ_REQ_MADE] =
+            true; /* Indicates that at least one read request has been made */
+        cronet_bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer,
+                                         stream_state->rs.remaining_bytes);
+        result = ACTION_TAKEN_WITH_CALLBACK;
+      } else {
+        result = NO_ACTION_POSSIBLE;
+      }
+    } else if (stream_state->rs.remaining_bytes == 0) {
+      CRONET_LOG(GPR_DEBUG, "read operation complete");
+      gpr_slice read_data_slice =
+          gpr_slice_malloc((uint32_t)stream_state->rs.length_field);
+      uint8_t *dst_p = GPR_SLICE_START_PTR(read_data_slice);
+      memcpy(dst_p, stream_state->rs.read_buffer,
+             (size_t)stream_state->rs.length_field);
+      gpr_slice_buffer_init(&stream_state->rs.read_slice_buffer);
+      gpr_slice_buffer_add(&stream_state->rs.read_slice_buffer,
+                           read_data_slice);
+      grpc_slice_buffer_stream_init(&stream_state->rs.sbs,
+                                    &stream_state->rs.read_slice_buffer, 0);
+      *((grpc_byte_buffer **)stream_op->recv_message) =
+          (grpc_byte_buffer *)&stream_state->rs.sbs;
+      grpc_exec_ctx_sched(exec_ctx, stream_op->recv_message_ready,
+                          GRPC_ERROR_NONE, NULL);
+      stream_state->state_op_done[OP_RECV_MESSAGE] = true;
+      oas->state.state_op_done[OP_RECV_MESSAGE] = true;
+      /* Clear read state of the stream, so next read op (if it were to come)
+       * will work */
+      stream_state->rs.received_bytes = stream_state->rs.remaining_bytes =
+          stream_state->rs.length_field_received = 0;
+      result = ACTION_TAKEN_NO_CALLBACK;
+    }
+  } else if (stream_op->recv_trailing_metadata &&
+             op_can_be_run(stream_op, stream_state, &oas->state,
+                           OP_RECV_TRAILING_METADATA)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p  OP_RECV_TRAILING_METADATA", oas);
+    if (oas->s->state.rs.trailing_metadata_valid) {
+      grpc_chttp2_incoming_metadata_buffer_publish(
+          &oas->s->state.rs.trailing_metadata,
+          stream_op->recv_trailing_metadata);
+      stream_state->rs.trailing_metadata_valid = false;
+    }
+    stream_state->state_op_done[OP_RECV_TRAILING_METADATA] = true;
+    result = ACTION_TAKEN_NO_CALLBACK;
+  } else if (stream_op->send_trailing_metadata &&
+             op_can_be_run(stream_op, stream_state, &oas->state,
+                           OP_SEND_TRAILING_METADATA)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p  OP_SEND_TRAILING_METADATA", oas);
+    if (stream_state->state_callback_received[OP_FAILED]) {
+      result = NO_ACTION_POSSIBLE;
+      CRONET_LOG(GPR_DEBUG, "Stream is either cancelled or failed.");
+    } else {
+      CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_write (%p, 0)",
+                 s->cbs);
+      stream_state->state_callback_received[OP_SEND_MESSAGE] = false;
+      cronet_bidirectional_stream_write(s->cbs, "", 0, true);
+      result = ACTION_TAKEN_WITH_CALLBACK;
+    }
+    stream_state->state_op_done[OP_SEND_TRAILING_METADATA] = true;
+  } else if (stream_op->cancel_error &&
+             op_can_be_run(stream_op, stream_state, &oas->state,
+                           OP_CANCEL_ERROR)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p  OP_CANCEL_ERROR", oas);
+    CRONET_LOG(GPR_DEBUG, "W: cronet_bidirectional_stream_cancel(%p)", s->cbs);
+    if (s->cbs) {
+      cronet_bidirectional_stream_cancel(s->cbs);
+    }
+    stream_state->state_op_done[OP_CANCEL_ERROR] = true;
+    result = ACTION_TAKEN_WITH_CALLBACK;
+  } else if (stream_op->on_complete &&
+             op_can_be_run(stream_op, stream_state, &oas->state,
+                           OP_ON_COMPLETE)) {
+    /* All actions in this stream_op are complete. Call the on_complete callback
+     */
+    CRONET_LOG(GPR_DEBUG, "running: %p  OP_ON_COMPLETE", oas);
+    grpc_exec_ctx_sched(exec_ctx, stream_op->on_complete, GRPC_ERROR_NONE,
+                        NULL);
+    oas->state.state_op_done[OP_ON_COMPLETE] = true;
+    oas->done = true;
+    /* reset any send message state, only if this ON_COMPLETE is about a send.
+     */
+    if (stream_op->send_message) {
+      stream_state->state_callback_received[OP_SEND_MESSAGE] = false;
+      stream_state->state_op_done[OP_SEND_MESSAGE] = false;
+    }
+    result = ACTION_TAKEN_NO_CALLBACK;
+    /* If this is the on_complete callback being called for a received message -
+      make a note */
+    if (stream_op->recv_message)
+      stream_state->state_op_done[OP_RECV_MESSAGE_AND_ON_COMPLETE] = true;
+  } else {
+    result = NO_ACTION_POSSIBLE;
   }
+  return result;
 }
 
+/*
+  Functions used by upper layers to access transport functionality.
+*/
+
 static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
                        grpc_stream *gs, grpc_stream_refcount *refcount,
                        const void *server_data) {
   stream_obj *s = (stream_obj *)gs;
-  memset(s->callback_list, 0, sizeof(s->callback_list));
+  memset(&s->storage, 0, sizeof(s->storage));
+  s->storage.head = NULL;
+  memset(&s->state, 0, sizeof(s->state));
+  s->curr_op = NULL;
   s->cbs = NULL;
-  gpr_mu_init(&s->recv_mu);
-  s->read_buffer = gpr_malloc(GRPC_HEADER_SIZE_IN_BYTES);
-  s->write_buffer = gpr_malloc(GRPC_HEADER_SIZE_IN_BYTES);
-  gpr_slice_buffer_init(&s->write_slice_buffer);
-  if (grpc_cronet_trace) {
-    gpr_log(GPR_DEBUG, "cronet_transport - init_stream");
-  }
+  memset(&s->header_array, 0, sizeof(s->header_array));
+  memset(&s->state.rs, 0, sizeof(s->state.rs));
+  memset(&s->state.ws, 0, sizeof(s->state.ws));
+  memset(s->state.state_op_done, 0, sizeof(s->state.state_op_done));
+  memset(s->state.state_callback_received, 0,
+         sizeof(s->state.state_callback_received));
+  gpr_mu_init(&s->mu);
   return 0;
 }
 
-static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
-                           grpc_stream *gs, void *and_free_memory) {
-  if (grpc_cronet_trace) {
-    gpr_log(GPR_DEBUG, "Destroy stream");
-  }
+static void set_pollset_do_nothing(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
+                                   grpc_stream *gs, grpc_pollset *pollset) {}
+
+static void set_pollset_set_do_nothing(grpc_exec_ctx *exec_ctx,
+                                       grpc_transport *gt, grpc_stream *gs,
+                                       grpc_pollset_set *pollset_set) {}
+
+static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
+                              grpc_stream *gs, grpc_transport_stream_op *op) {
+  CRONET_LOG(GPR_DEBUG, "perform_stream_op");
   stream_obj *s = (stream_obj *)gs;
-  s->cbs = NULL;
-  gpr_free(s->read_buffer);
-  gpr_free(s->write_buffer);
-  gpr_free(s->url);
-  gpr_mu_destroy(&s->recv_mu);
-  if (and_free_memory) {
-    gpr_free(and_free_memory);
-  }
+  s->curr_gs = gs;
+  memcpy(&s->curr_ct, gt, sizeof(grpc_cronet_transport));
+  add_to_storage(s, op);
+  execute_from_storage(s);
 }
 
-static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) {
-  grpc_cronet_transport *ct = (grpc_cronet_transport *)gt;
-  gpr_free(ct->host);
-  if (grpc_cronet_trace) {
-    gpr_log(GPR_DEBUG, "Destroy transport");
-  }
+static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
+                           grpc_stream *gs, void *and_free_memory) {}
+
+static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) {}
+
+static char *get_peer(grpc_exec_ctx *exec_ctx, grpc_transport *gt) {
+  return NULL;
 }
 
+static void perform_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
+                       grpc_transport_op *op) {}
+
 const grpc_transport_vtable grpc_cronet_vtable = {sizeof(stream_obj),
                                                   "cronet_http",
                                                   init_stream,
                                                   set_pollset_do_nothing,
                                                   set_pollset_set_do_nothing,
                                                   perform_stream_op,
-                                                  NULL,
+                                                  perform_op,
                                                   destroy_stream,
                                                   destroy_transport,
-                                                  NULL};
+                                                  get_peer};
diff --git a/src/core/lib/channel/channel_args.c b/src/core/lib/channel/channel_args.c
index 79ceeb6..3a56b1f 100644
--- a/src/core/lib/channel/channel_args.c
+++ b/src/core/lib/channel/channel_args.c
@@ -271,3 +271,21 @@
   }
   return 0;
 }
+
+int grpc_channel_arg_get_integer(grpc_arg *arg, grpc_integer_options options) {
+  if (arg->type != GRPC_ARG_INTEGER) {
+    gpr_log(GPR_ERROR, "%s ignored: it must be an integer", arg->key);
+    return options.default_value;
+  }
+  if (arg->value.integer < options.min_value) {
+    gpr_log(GPR_ERROR, "%s ignored: it must be >= %d", arg->key,
+            options.min_value);
+    return options.default_value;
+  }
+  if (arg->value.integer > options.max_value) {
+    gpr_log(GPR_ERROR, "%s ignored: it must be <= %d", arg->key,
+            options.max_value);
+    return options.default_value;
+  }
+  return arg->value.integer;
+}
diff --git a/src/core/lib/channel/channel_args.h b/src/core/lib/channel/channel_args.h
index aec61ee..586a296 100644
--- a/src/core/lib/channel/channel_args.h
+++ b/src/core/lib/channel/channel_args.h
@@ -89,4 +89,12 @@
 int grpc_channel_args_compare(const grpc_channel_args *a,
                               const grpc_channel_args *b);
 
+typedef struct grpc_integer_options {
+  int default_value;  // Return this if value is outside of expected bounds.
+  int min_value;
+  int max_value;
+} grpc_integer_options;
+/** Returns the value of \a arg, subject to the contraints in \a options. */
+int grpc_channel_arg_get_integer(grpc_arg *arg, grpc_integer_options options);
+
 #endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H */
diff --git a/src/core/lib/channel/channel_stack.c b/src/core/lib/channel/channel_stack.c
index 87175d7..57d34d9 100644
--- a/src/core/lib/channel/channel_stack.c
+++ b/src/core/lib/channel/channel_stack.c
@@ -32,6 +32,7 @@
  */
 
 #include "src/core/lib/channel/channel_stack.h"
+#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
 #include <stdlib.h>
@@ -157,12 +158,11 @@
   }
 }
 
-void grpc_call_stack_init(grpc_exec_ctx *exec_ctx,
-                          grpc_channel_stack *channel_stack, int initial_refs,
-                          grpc_iomgr_cb_func destroy, void *destroy_arg,
-                          grpc_call_context_element *context,
-                          const void *transport_server_data,
-                          grpc_call_stack *call_stack) {
+grpc_error *grpc_call_stack_init(
+    grpc_exec_ctx *exec_ctx, grpc_channel_stack *channel_stack,
+    int initial_refs, grpc_iomgr_cb_func destroy, void *destroy_arg,
+    grpc_call_context_element *context, const void *transport_server_data,
+    gpr_timespec deadline, grpc_call_stack *call_stack) {
   grpc_channel_element *channel_elems = CHANNEL_ELEMS_FROM_STACK(channel_stack);
   grpc_call_element_args args;
   size_t count = channel_stack->count;
@@ -178,17 +178,28 @@
               ROUND_UP_TO_ALIGNMENT_SIZE(count * sizeof(grpc_call_element));
 
   /* init per-filter data */
+  grpc_error *first_error = GRPC_ERROR_NONE;
   for (i = 0; i < count; i++) {
     args.call_stack = call_stack;
     args.server_transport_data = transport_server_data;
     args.context = context;
+    args.deadline = deadline;
     call_elems[i].filter = channel_elems[i].filter;
     call_elems[i].channel_data = channel_elems[i].channel_data;
     call_elems[i].call_data = user_data;
-    call_elems[i].filter->init_call_elem(exec_ctx, &call_elems[i], &args);
+    grpc_error *error =
+        call_elems[i].filter->init_call_elem(exec_ctx, &call_elems[i], &args);
+    if (error != GRPC_ERROR_NONE) {
+      if (first_error == GRPC_ERROR_NONE) {
+        first_error = error;
+      } else {
+        GRPC_ERROR_UNREF(error);
+      }
+    }
     user_data +=
         ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data);
   }
+  return first_error;
 }
 
 void grpc_call_stack_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
@@ -217,7 +228,7 @@
     grpc_polling_entity *pollent) {}
 
 void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack,
-                             const grpc_call_stats *call_stats,
+                             const grpc_call_final_info *final_info,
                              void *and_free_memory) {
   grpc_call_element *elems = CALL_ELEMS_FROM_STACK(stack);
   size_t count = stack->count;
@@ -225,7 +236,7 @@
 
   /* destroy per-filter data */
   for (i = 0; i < count; i++) {
-    elems[i].filter->destroy_call_elem(exec_ctx, &elems[i], call_stats,
+    elems[i].filter->destroy_call_elem(exec_ctx, &elems[i], final_info,
                                        i == count - 1 ? and_free_memory : NULL);
   }
 }
@@ -259,21 +270,38 @@
       sizeof(grpc_call_stack)));
 }
 
+static void destroy_op(grpc_exec_ctx *exec_ctx, void *op, grpc_error *error) {
+  gpr_free(op);
+}
+
 void grpc_call_element_send_cancel(grpc_exec_ctx *exec_ctx,
-                                   grpc_call_element *cur_elem) {
-  grpc_transport_stream_op op;
-  memset(&op, 0, sizeof(op));
-  op.cancel_error = GRPC_ERROR_CANCELLED;
-  grpc_call_next_op(exec_ctx, cur_elem, &op);
+                                   grpc_call_element *elem) {
+  grpc_transport_stream_op *op = gpr_malloc(sizeof(*op));
+  memset(op, 0, sizeof(*op));
+  op->cancel_error = GRPC_ERROR_CANCELLED;
+  op->on_complete = grpc_closure_create(destroy_op, op);
+  elem->filter->start_transport_stream_op(exec_ctx, elem, op);
 }
 
 void grpc_call_element_send_cancel_with_message(grpc_exec_ctx *exec_ctx,
-                                                grpc_call_element *cur_elem,
+                                                grpc_call_element *elem,
                                                 grpc_status_code status,
                                                 gpr_slice *optional_message) {
-  grpc_transport_stream_op op;
-  memset(&op, 0, sizeof(op));
-  grpc_transport_stream_op_add_cancellation_with_message(&op, status,
+  grpc_transport_stream_op *op = gpr_malloc(sizeof(*op));
+  memset(op, 0, sizeof(*op));
+  op->on_complete = grpc_closure_create(destroy_op, op);
+  grpc_transport_stream_op_add_cancellation_with_message(op, status,
                                                          optional_message);
-  grpc_call_next_op(exec_ctx, cur_elem, &op);
+  elem->filter->start_transport_stream_op(exec_ctx, elem, op);
+}
+
+void grpc_call_element_send_close_with_message(grpc_exec_ctx *exec_ctx,
+                                               grpc_call_element *elem,
+                                               grpc_status_code status,
+                                               gpr_slice *optional_message) {
+  grpc_transport_stream_op *op = gpr_malloc(sizeof(*op));
+  memset(op, 0, sizeof(*op));
+  op->on_complete = grpc_closure_create(destroy_op, op);
+  grpc_transport_stream_op_add_close(op, status, optional_message);
+  elem->filter->start_transport_stream_op(exec_ctx, elem, op);
 }
diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h
index d72c015..1cfe288 100644
--- a/src/core/lib/channel/channel_stack.h
+++ b/src/core/lib/channel/channel_stack.h
@@ -51,6 +51,10 @@
 #include "src/core/lib/iomgr/polling_entity.h"
 #include "src/core/lib/transport/transport.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 typedef struct grpc_channel_element grpc_channel_element;
 typedef struct grpc_call_element grpc_call_element;
 
@@ -70,14 +74,20 @@
   grpc_call_stack *call_stack;
   const void *server_transport_data;
   grpc_call_context_element *context;
+  gpr_timespec deadline;
 } grpc_call_element_args;
 
 typedef struct {
   grpc_transport_stream_stats transport_stream_stats;
   gpr_timespec latency; /* From call creating to enqueing of received status */
-  grpc_status_code final_status;
 } grpc_call_stats;
 
+/** Information about the call upon completion. */
+typedef struct {
+  grpc_call_stats stats;
+  grpc_status_code final_status;
+} grpc_call_final_info;
+
 /* Channel filters specify:
    1. the amount of memory needed in the channel & call (via the sizeof_XXX
       members)
@@ -110,8 +120,9 @@
      on a client; if it is non-NULL, then it points to memory owned by the
      transport and is on the server. Most filters want to ignore this
      argument. */
-  void (*init_call_elem)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                         grpc_call_element_args *args);
+  grpc_error *(*init_call_elem)(grpc_exec_ctx *exec_ctx,
+                                grpc_call_element *elem,
+                                grpc_call_element_args *args);
   void (*set_pollset_or_pollset_set)(grpc_exec_ctx *exec_ctx,
                                      grpc_call_element *elem,
                                      grpc_polling_entity *pollent);
@@ -119,16 +130,17 @@
      The filter does not need to do any chaining.
      The bottom filter of a stack will be passed a non-NULL pointer to
      \a and_free_memory that should be passed to gpr_free when destruction
-     is complete. */
+     is complete. \a final_info contains data about the completed call, mainly
+     for reporting purposes. */
   void (*destroy_call_elem)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                            const grpc_call_stats *stats,
+                            const grpc_call_final_info *final_info,
                             void *and_free_memory);
 
   /* sizeof(per channel data) */
   size_t sizeof_channel_data;
   /* Initialize per-channel data.
-     elem is initialized at the start of the call, and elem->channel_data is
-     what needs initializing.
+     elem is initialized at the creating of the channel, and elem->channel_data
+     is what needs initializing.
      is_first, is_last designate this elements position in the stack, and are
      useful for asserting correct configuration by upper layer code.
      The filter does not need to do any chaining */
@@ -209,12 +221,11 @@
 /* Initialize a call stack given a channel stack. transport_server_data is
    expected to be NULL on a client, or an opaque transport owned pointer on the
    server. */
-void grpc_call_stack_init(grpc_exec_ctx *exec_ctx,
-                          grpc_channel_stack *channel_stack, int initial_refs,
-                          grpc_iomgr_cb_func destroy, void *destroy_arg,
-                          grpc_call_context_element *context,
-                          const void *transport_server_data,
-                          grpc_call_stack *call_stack);
+grpc_error *grpc_call_stack_init(
+    grpc_exec_ctx *exec_ctx, grpc_channel_stack *channel_stack,
+    int initial_refs, grpc_iomgr_cb_func destroy, void *destroy_arg,
+    grpc_call_context_element *context, const void *transport_server_data,
+    gpr_timespec deadline, grpc_call_stack *call_stack);
 /* Set a pollset or a pollset_set for a call stack: must occur before the first
  * op is started */
 void grpc_call_stack_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
@@ -243,7 +254,7 @@
 
 /* Destroy a call stack */
 void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack,
-                             const grpc_call_stats *call_stats,
+                             const grpc_call_final_info *final_info,
                              void *and_free_memory);
 
 /* Ignore set pollset{_set} - used by filters if they don't care about pollsets
@@ -278,9 +289,18 @@
                                                 grpc_status_code status,
                                                 gpr_slice *optional_message);
 
+void grpc_call_element_send_close_with_message(grpc_exec_ctx *exec_ctx,
+                                               grpc_call_element *cur_elem,
+                                               grpc_status_code status,
+                                               gpr_slice *optional_message);
+
 extern int grpc_trace_channel;
 
 #define GRPC_CALL_LOG_OP(sev, elem, op) \
   if (grpc_trace_channel) grpc_call_log_op(sev, elem, op)
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H */
diff --git a/src/core/lib/channel/channel_stack_builder.h b/src/core/lib/channel/channel_stack_builder.h
index 0e6bfd9..4a00f7b 100644
--- a/src/core/lib/channel/channel_stack_builder.h
+++ b/src/core/lib/channel/channel_stack_builder.h
@@ -39,6 +39,10 @@
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/channel_stack.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /// grpc_channel_stack_builder offers a programmatic interface to selected
 /// and order channel filters
 typedef struct grpc_channel_stack_builder grpc_channel_stack_builder;
@@ -158,4 +162,8 @@
 
 extern int grpc_trace_channel_stack_builder;
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H */
diff --git a/src/core/lib/channel/compress_filter.c b/src/core/lib/channel/compress_filter.c
index 32ebe53..0981d59 100644
--- a/src/core/lib/channel/compress_filter.c
+++ b/src/core/lib/channel/compress_filter.c
@@ -60,7 +60,7 @@
   /** If true, contents of \a compression_algorithm are authoritative */
   int has_compression_algorithm;
 
-  grpc_transport_stream_op send_op;
+  grpc_transport_stream_op *send_op;
   uint32_t send_length;
   uint32_t send_flags;
   gpr_slice incoming_slice;
@@ -199,11 +199,11 @@
 
   grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices,
                                 calld->send_flags);
-  calld->send_op.send_message = &calld->replacement_stream.base;
-  calld->post_send = calld->send_op.on_complete;
-  calld->send_op.on_complete = &calld->send_done;
+  calld->send_op->send_message = &calld->replacement_stream.base;
+  calld->post_send = calld->send_op->on_complete;
+  calld->send_op->on_complete = &calld->send_done;
 
-  grpc_call_next_op(exec_ctx, elem, &calld->send_op);
+  grpc_call_next_op(exec_ctx, elem, calld->send_op);
 }
 
 static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
@@ -220,7 +220,7 @@
 static void continue_send_message(grpc_exec_ctx *exec_ctx,
                                   grpc_call_element *elem) {
   call_data *calld = elem->call_data;
-  while (grpc_byte_stream_next(exec_ctx, calld->send_op.send_message,
+  while (grpc_byte_stream_next(exec_ctx, calld->send_op->send_message,
                                &calld->incoming_slice, ~(size_t)0,
                                &calld->got_slice)) {
     gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
@@ -243,7 +243,7 @@
   }
   if (op->send_message != NULL && !skip_compression(elem) &&
       0 == (op->send_message->flags & GRPC_WRITE_NO_COMPRESS)) {
-    calld->send_op = *op;
+    calld->send_op = op;
     calld->send_length = op->send_message->length;
     calld->send_flags = op->send_message->flags;
     continue_send_message(exec_ctx, elem);
@@ -256,8 +256,9 @@
 }
 
 /* Constructor for call_data */
-static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {
+static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  grpc_call_element_args *args) {
   /* grab pointers to our data from the call element */
   call_data *calld = elem->call_data;
 
@@ -266,11 +267,14 @@
   calld->has_compression_algorithm = 0;
   grpc_closure_init(&calld->got_slice, got_slice, elem);
   grpc_closure_init(&calld->send_done, send_done, elem);
+
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {
   /* grab pointers to our data from the call element */
   call_data *calld = elem->call_data;
   gpr_slice_buffer_destroy(&calld->slices);
diff --git a/src/core/lib/channel/connected_channel.c b/src/core/lib/channel/connected_channel.c
index 0a7d27a..918379c 100644
--- a/src/core/lib/channel/connected_channel.c
+++ b/src/core/lib/channel/connected_channel.c
@@ -81,16 +81,16 @@
 }
 
 /* Constructor for call_data */
-static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {
+static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  grpc_call_element_args *args) {
   call_data *calld = elem->call_data;
   channel_data *chand = elem->channel_data;
-  int r;
-
-  r = grpc_transport_init_stream(
+  int r = grpc_transport_init_stream(
       exec_ctx, chand->transport, TRANSPORT_STREAM_FROM_CALL_DATA(calld),
       &args->call_stack->refcount, args->server_transport_data);
-  GPR_ASSERT(r == 0);
+  return r == 0 ? GRPC_ERROR_NONE
+                : GRPC_ERROR_CREATE("transport stream initialization failed");
 }
 
 static void set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
@@ -104,7 +104,7 @@
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats,
+                              const grpc_call_final_info *final_info,
                               void *and_free_memory) {
   call_data *calld = elem->call_data;
   channel_data *chand = elem->channel_data;
diff --git a/src/core/lib/channel/context.h b/src/core/lib/channel/context.h
index c50e842..071c5f6 100644
--- a/src/core/lib/channel/context.h
+++ b/src/core/lib/channel/context.h
@@ -34,10 +34,19 @@
 #ifndef GRPC_CORE_LIB_CHANNEL_CONTEXT_H
 #define GRPC_CORE_LIB_CHANNEL_CONTEXT_H
 
-/* Call object context pointers */
+/// Call object context pointers.
+
+/// Call context is represented as an array of \a grpc_call_context_elements.
+/// This enum represents the indexes into the array, where each index
+/// contains a different type of value.
 typedef enum {
+  /// Value is either a \a grpc_client_security_context or a
+  /// \a grpc_server_security_context.
   GRPC_CONTEXT_SECURITY = 0,
+
+  /// Value is a \a census_context.
   GRPC_CONTEXT_TRACING,
+
   GRPC_CONTEXT_COUNT
 } grpc_context_index;
 
diff --git a/src/core/lib/channel/deadline_filter.c b/src/core/lib/channel/deadline_filter.c
new file mode 100644
index 0000000..079b98a
--- /dev/null
+++ b/src/core/lib/channel/deadline_filter.c
@@ -0,0 +1,302 @@
+//
+// Copyright 2016, 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/lib/channel/deadline_filter.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/timer.h"
+
+//
+// grpc_deadline_state
+//
+
+// Timer callback.
+static void timer_callback(grpc_exec_ctx* exec_ctx, void* arg,
+                           grpc_error* error) {
+  grpc_call_element* elem = arg;
+  grpc_deadline_state* deadline_state = elem->call_data;
+  gpr_mu_lock(&deadline_state->timer_mu);
+  deadline_state->timer_pending = false;
+  gpr_mu_unlock(&deadline_state->timer_mu);
+  if (error != GRPC_ERROR_CANCELLED) {
+    gpr_slice msg = gpr_slice_from_static_string("Deadline Exceeded");
+    grpc_call_element_send_cancel_with_message(
+        exec_ctx, elem, GRPC_STATUS_DEADLINE_EXCEEDED, &msg);
+    gpr_slice_unref(msg);
+  }
+  GRPC_CALL_STACK_UNREF(exec_ctx, deadline_state->call_stack, "deadline_timer");
+}
+
+// Starts the deadline timer.
+static void start_timer_if_needed(grpc_exec_ctx* exec_ctx,
+                                  grpc_call_element* elem,
+                                  gpr_timespec deadline) {
+  grpc_deadline_state* deadline_state = elem->call_data;
+  deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
+  if (gpr_time_cmp(deadline, gpr_inf_future(GPR_CLOCK_MONOTONIC)) != 0) {
+    // Take a reference to the call stack, to be owned by the timer.
+    GRPC_CALL_STACK_REF(deadline_state->call_stack, "deadline_timer");
+    gpr_mu_lock(&deadline_state->timer_mu);
+    deadline_state->timer_pending = true;
+    grpc_timer_init(exec_ctx, &deadline_state->timer, deadline, timer_callback,
+                    elem, gpr_now(GPR_CLOCK_MONOTONIC));
+    gpr_mu_unlock(&deadline_state->timer_mu);
+  }
+}
+
+// Cancels the deadline timer.
+static void cancel_timer_if_needed(grpc_exec_ctx* exec_ctx,
+                                   grpc_deadline_state* deadline_state) {
+  gpr_mu_lock(&deadline_state->timer_mu);
+  if (deadline_state->timer_pending) {
+    grpc_timer_cancel(exec_ctx, &deadline_state->timer);
+    deadline_state->timer_pending = false;
+  }
+  gpr_mu_unlock(&deadline_state->timer_mu);
+}
+
+// Callback run when the call is complete.
+static void on_complete(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) {
+  grpc_deadline_state* deadline_state = arg;
+  cancel_timer_if_needed(exec_ctx, deadline_state);
+  // Invoke the next callback.
+  deadline_state->next_on_complete->cb(
+      exec_ctx, deadline_state->next_on_complete->cb_arg, error);
+}
+
+// Inject our own on_complete callback into op.
+static void inject_on_complete_cb(grpc_deadline_state* deadline_state,
+                                  grpc_transport_stream_op* op) {
+  deadline_state->next_on_complete = op->on_complete;
+  grpc_closure_init(&deadline_state->on_complete, on_complete, deadline_state);
+  op->on_complete = &deadline_state->on_complete;
+}
+
+// Callback and associated state for starting the timer after call stack
+// initialization has been completed.
+struct start_timer_after_init_state {
+  grpc_call_element* elem;
+  gpr_timespec deadline;
+  grpc_closure closure;
+};
+static void start_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg,
+                                   grpc_error* error) {
+  struct start_timer_after_init_state* state = arg;
+  start_timer_if_needed(exec_ctx, state->elem, state->deadline);
+  gpr_free(state);
+}
+
+void grpc_deadline_state_init(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
+                              grpc_call_element_args* args) {
+  grpc_deadline_state* deadline_state = elem->call_data;
+  memset(deadline_state, 0, sizeof(*deadline_state));
+  deadline_state->call_stack = args->call_stack;
+  gpr_mu_init(&deadline_state->timer_mu);
+  // Deadline will always be infinite on servers, so the timer will only be
+  // set on clients with a finite deadline.
+  const gpr_timespec deadline =
+      gpr_convert_clock_type(args->deadline, GPR_CLOCK_MONOTONIC);
+  if (gpr_time_cmp(deadline, gpr_inf_future(GPR_CLOCK_MONOTONIC)) != 0) {
+    // When the deadline passes, we indicate the failure by sending down
+    // an op with cancel_error set.  However, we can't send down any ops
+    // until after the call stack is fully initialized.  If we start the
+    // timer here, we have no guarantee that the timer won't pop before
+    // call stack initialization is finished.  To avoid that problem, we
+    // create a closure to start the timer, and we schedule that closure
+    // to be run after call stack initialization is done.
+    struct start_timer_after_init_state* state = gpr_malloc(sizeof(*state));
+    state->elem = elem;
+    state->deadline = deadline;
+    grpc_closure_init(&state->closure, start_timer_after_init, state);
+    grpc_exec_ctx_sched(exec_ctx, &state->closure, GRPC_ERROR_NONE, NULL);
+  }
+}
+
+void grpc_deadline_state_destroy(grpc_exec_ctx* exec_ctx,
+                                 grpc_call_element* elem) {
+  grpc_deadline_state* deadline_state = elem->call_data;
+  cancel_timer_if_needed(exec_ctx, deadline_state);
+  gpr_mu_destroy(&deadline_state->timer_mu);
+}
+
+void grpc_deadline_state_client_start_transport_stream_op(
+    grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
+    grpc_transport_stream_op* op) {
+  grpc_deadline_state* deadline_state = elem->call_data;
+  if (op->cancel_error != GRPC_ERROR_NONE ||
+      op->close_error != GRPC_ERROR_NONE) {
+    cancel_timer_if_needed(exec_ctx, deadline_state);
+  } else {
+    // Make sure we know when the call is complete, so that we can cancel
+    // the timer.
+    if (op->recv_trailing_metadata != NULL) {
+      inject_on_complete_cb(deadline_state, op);
+    }
+  }
+}
+
+//
+// filter code
+//
+
+// Constructor for channel_data.  Used for both client and server filters.
+static void init_channel_elem(grpc_exec_ctx* exec_ctx,
+                              grpc_channel_element* elem,
+                              grpc_channel_element_args* args) {
+  GPR_ASSERT(!args->is_last);
+}
+
+// Destructor for channel_data.  Used for both client and server filters.
+static void destroy_channel_elem(grpc_exec_ctx* exec_ctx,
+                                 grpc_channel_element* elem) {}
+
+// Call data used for both client and server filter.
+typedef struct base_call_data {
+  grpc_deadline_state deadline_state;
+} base_call_data;
+
+// Additional call data used only for the server filter.
+typedef struct server_call_data {
+  base_call_data base;  // Must be first.
+  // The closure for receiving initial metadata.
+  grpc_closure recv_initial_metadata_ready;
+  // Received initial metadata batch.
+  grpc_metadata_batch* recv_initial_metadata;
+  // The original recv_initial_metadata_ready closure, which we chain to
+  // after our own closure is invoked.
+  grpc_closure* next_recv_initial_metadata_ready;
+} server_call_data;
+
+// Constructor for call_data.  Used for both client and server filters.
+static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx,
+                                  grpc_call_element* elem,
+                                  grpc_call_element_args* args) {
+  // Note: size of call data is different between client and server.
+  memset(elem->call_data, 0, elem->filter->sizeof_call_data);
+  grpc_deadline_state_init(exec_ctx, elem, args);
+  return GRPC_ERROR_NONE;
+}
+
+// Destructor for call_data.  Used for both client and server filters.
+static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              void* and_free_memory) {
+  grpc_deadline_state_destroy(exec_ctx, elem);
+}
+
+// Method for starting a call op for client filter.
+static void client_start_transport_stream_op(grpc_exec_ctx* exec_ctx,
+                                             grpc_call_element* elem,
+                                             grpc_transport_stream_op* op) {
+  grpc_deadline_state_client_start_transport_stream_op(exec_ctx, elem, op);
+  // Chain to next filter.
+  grpc_call_next_op(exec_ctx, elem, op);
+}
+
+// Callback for receiving initial metadata on the server.
+static void recv_initial_metadata_ready(grpc_exec_ctx* exec_ctx, void* arg,
+                                        grpc_error* error) {
+  grpc_call_element* elem = arg;
+  server_call_data* calld = elem->call_data;
+  // Get deadline from metadata and start the timer if needed.
+  start_timer_if_needed(exec_ctx, elem, calld->recv_initial_metadata->deadline);
+  // Invoke the next callback.
+  calld->next_recv_initial_metadata_ready->cb(
+      exec_ctx, calld->next_recv_initial_metadata_ready->cb_arg, error);
+}
+
+// Method for starting a call op for server filter.
+static void server_start_transport_stream_op(grpc_exec_ctx* exec_ctx,
+                                             grpc_call_element* elem,
+                                             grpc_transport_stream_op* op) {
+  server_call_data* calld = elem->call_data;
+  if (op->cancel_error != GRPC_ERROR_NONE ||
+      op->close_error != GRPC_ERROR_NONE) {
+    cancel_timer_if_needed(exec_ctx, &calld->base.deadline_state);
+  } else {
+    // If we're receiving initial metadata, we need to get the deadline
+    // from the recv_initial_metadata_ready callback.  So we inject our
+    // own callback into that hook.
+    if (op->recv_initial_metadata_ready != NULL) {
+      calld->next_recv_initial_metadata_ready = op->recv_initial_metadata_ready;
+      calld->recv_initial_metadata = op->recv_initial_metadata;
+      grpc_closure_init(&calld->recv_initial_metadata_ready,
+                        recv_initial_metadata_ready, elem);
+      op->recv_initial_metadata_ready = &calld->recv_initial_metadata_ready;
+    }
+    // Make sure we know when the call is complete, so that we can cancel
+    // the timer.
+    // Note that we trigger this on recv_trailing_metadata, even though
+    // the client never sends trailing metadata, because this is the
+    // hook that tells us when the call is complete on the server side.
+    if (op->recv_trailing_metadata != NULL) {
+      inject_on_complete_cb(&calld->base.deadline_state, op);
+    }
+  }
+  // Chain to next filter.
+  grpc_call_next_op(exec_ctx, elem, op);
+}
+
+const grpc_channel_filter grpc_client_deadline_filter = {
+    client_start_transport_stream_op,
+    grpc_channel_next_op,
+    sizeof(base_call_data),
+    init_call_elem,
+    grpc_call_stack_ignore_set_pollset_or_pollset_set,
+    destroy_call_elem,
+    0,  // sizeof(channel_data)
+    init_channel_elem,
+    destroy_channel_elem,
+    grpc_call_next_get_peer,
+    "deadline",
+};
+
+const grpc_channel_filter grpc_server_deadline_filter = {
+    server_start_transport_stream_op,
+    grpc_channel_next_op,
+    sizeof(server_call_data),
+    init_call_elem,
+    grpc_call_stack_ignore_set_pollset_or_pollset_set,
+    destroy_call_elem,
+    0,  // sizeof(channel_data)
+    init_channel_elem,
+    destroy_channel_elem,
+    grpc_call_next_get_peer,
+    "deadline",
+};
diff --git a/src/core/lib/channel/deadline_filter.h b/src/core/lib/channel/deadline_filter.h
new file mode 100644
index 0000000..685df87
--- /dev/null
+++ b/src/core/lib/channel/deadline_filter.h
@@ -0,0 +1,79 @@
+//
+// Copyright 2016, 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_CORE_LIB_CHANNEL_DEADLINE_FILTER_H
+#define GRPC_CORE_LIB_CHANNEL_DEADLINE_FILTER_H
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/iomgr/timer.h"
+
+// State used for filters that enforce call deadlines.
+// Must be the first field in the filter's call_data.
+typedef struct grpc_deadline_state {
+  // We take a reference to the call stack for the timer callback.
+  grpc_call_stack* call_stack;
+  // Guards access to timer_pending and timer.
+  gpr_mu timer_mu;
+  // True if the timer callback is currently pending.
+  bool timer_pending;
+  // The deadline timer.
+  grpc_timer timer;
+  // Closure to invoke when the call is complete.
+  // We use this to cancel the timer.
+  grpc_closure on_complete;
+  // The original on_complete closure, which we chain to after our own
+  // closure is invoked.
+  grpc_closure* next_on_complete;
+} grpc_deadline_state;
+
+// To be used in a filter's init_call_elem(), destroy_call_elem(), and
+// start_transport_stream_op() methods to enforce call deadlines.
+//
+// REQUIRES: The first field in elem->call_data is a grpc_deadline_state.
+//
+// For grpc_deadline_state_client_start_transport_stream_op(), it is the
+// caller's responsibility to chain to the next filter if necessary
+// after the function returns.
+void grpc_deadline_state_init(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
+                              grpc_call_element_args* args);
+void grpc_deadline_state_destroy(grpc_exec_ctx* exec_ctx,
+                                 grpc_call_element* elem);
+void grpc_deadline_state_client_start_transport_stream_op(
+    grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
+    grpc_transport_stream_op* op);
+
+// Deadline filters for direct client channels and server channels.
+// Note: Deadlines for non-direct client channels are handled by the
+// client_channel filter.
+extern const grpc_channel_filter grpc_client_deadline_filter;
+extern const grpc_channel_filter grpc_server_deadline_filter;
+
+#endif /* GRPC_CORE_LIB_CHANNEL_DEADLINE_FILTER_H */
diff --git a/src/core/lib/channel/handshaker.c b/src/core/lib/channel/handshaker.c
index 6c3ca19..8f9fb17 100644
--- a/src/core/lib/channel/handshaker.c
+++ b/src/core/lib/channel/handshaker.c
@@ -33,8 +33,8 @@
 
 #include <string.h>
 
-#include <grpc/impl/codegen/alloc.h>
-#include <grpc/impl/codegen/log.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
 
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/handshaker.h"
@@ -62,11 +62,13 @@
                                   grpc_handshaker* handshaker,
                                   grpc_endpoint* endpoint,
                                   grpc_channel_args* args,
+                                  gpr_slice_buffer* read_buffer,
                                   gpr_timespec deadline,
                                   grpc_tcp_server_acceptor* acceptor,
                                   grpc_handshaker_done_cb cb, void* user_data) {
   handshaker->vtable->do_handshake(exec_ctx, handshaker, endpoint, args,
-                                   deadline, acceptor, cb, user_data);
+                                   read_buffer, deadline, acceptor, cb,
+                                   user_data);
 }
 
 //
@@ -143,7 +145,8 @@
 // handshakers together.
 static void call_next_handshaker(grpc_exec_ctx* exec_ctx,
                                  grpc_endpoint* endpoint,
-                                 grpc_channel_args* args, void* user_data,
+                                 grpc_channel_args* args,
+                                 gpr_slice_buffer* read_buffer, void* user_data,
                                  grpc_error* error) {
   grpc_handshake_manager* mgr = user_data;
   GPR_ASSERT(mgr->state != NULL);
@@ -151,8 +154,8 @@
   // If we got an error, skip all remaining handshakers and invoke the
   // caller-supplied callback immediately.
   if (error != GRPC_ERROR_NONE) {
-    mgr->state->final_cb(exec_ctx, endpoint, args, mgr->state->final_user_data,
-                         error);
+    mgr->state->final_cb(exec_ctx, endpoint, args, read_buffer,
+                         mgr->state->final_user_data, error);
     return;
   }
   grpc_handshaker_done_cb cb = call_next_handshaker;
@@ -163,9 +166,9 @@
     user_data = mgr->state->final_user_data;
   }
   // Invoke handshaker.
-  grpc_handshaker_do_handshake(exec_ctx, mgr->handshakers[mgr->state->index],
-                               endpoint, args, mgr->state->deadline,
-                               mgr->state->acceptor, cb, user_data);
+  grpc_handshaker_do_handshake(
+      exec_ctx, mgr->handshakers[mgr->state->index], endpoint, args,
+      read_buffer, mgr->state->deadline, mgr->state->acceptor, cb, user_data);
   ++mgr->state->index;
   // If this is the last handshaker, clean up state.
   if (mgr->state->index == mgr->count) {
@@ -180,10 +183,12 @@
     gpr_timespec deadline, grpc_tcp_server_acceptor* acceptor,
     grpc_handshaker_done_cb cb, void* user_data) {
   grpc_channel_args* args_copy = grpc_channel_args_copy(args);
+  gpr_slice_buffer* read_buffer = malloc(sizeof(*read_buffer));
+  gpr_slice_buffer_init(read_buffer);
   if (mgr->count == 0) {
     // No handshakers registered, so we just immediately call the done
     // callback with the passed-in endpoint.
-    cb(exec_ctx, endpoint, args_copy, user_data, GRPC_ERROR_NONE);
+    cb(exec_ctx, endpoint, args_copy, read_buffer, user_data, GRPC_ERROR_NONE);
   } else {
     GPR_ASSERT(mgr->state == NULL);
     mgr->state = gpr_malloc(sizeof(struct grpc_handshaker_state));
@@ -192,6 +197,7 @@
     mgr->state->acceptor = acceptor;
     mgr->state->final_cb = cb;
     mgr->state->final_user_data = user_data;
-    call_next_handshaker(exec_ctx, endpoint, args_copy, mgr, GRPC_ERROR_NONE);
+    call_next_handshaker(exec_ctx, endpoint, args_copy, read_buffer, mgr,
+                         GRPC_ERROR_NONE);
   }
 }
diff --git a/src/core/lib/channel/handshaker.h b/src/core/lib/channel/handshaker.h
index dfc469c..d574b46 100644
--- a/src/core/lib/channel/handshaker.h
+++ b/src/core/lib/channel/handshaker.h
@@ -35,7 +35,6 @@
 #define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H
 
 #include <grpc/impl/codegen/grpc_types.h>
-#include <grpc/impl/codegen/time.h>
 
 #include "src/core/lib/iomgr/closure.h"
 #include "src/core/lib/iomgr/endpoint.h"
@@ -56,10 +55,11 @@
 typedef struct grpc_handshaker grpc_handshaker;
 
 /// Callback type invoked when a handshaker is done.
-/// Takes ownership of \a args.
+/// Takes ownership of \a args and \a read_buffer.
 typedef void (*grpc_handshaker_done_cb)(grpc_exec_ctx* exec_ctx,
                                         grpc_endpoint* endpoint,
                                         grpc_channel_args* args,
+                                        gpr_slice_buffer* read_buffer,
                                         void* user_data, grpc_error* error);
 
 struct grpc_handshaker_vtable {
@@ -72,10 +72,12 @@
 
   /// Performs handshaking.  When finished, calls \a cb with \a user_data.
   /// Takes ownership of \a args.
+  /// Takes ownership of \a read_buffer, which contains leftover bytes read
+  /// from the endpoint by the previous handshaker.
   /// \a acceptor will be NULL for client-side handshakers.
   void (*do_handshake)(grpc_exec_ctx* exec_ctx, grpc_handshaker* handshaker,
                        grpc_endpoint* endpoint, grpc_channel_args* args,
-                       gpr_timespec deadline,
+                       gpr_slice_buffer* read_buffer, gpr_timespec deadline,
                        grpc_tcp_server_acceptor* acceptor,
                        grpc_handshaker_done_cb cb, void* user_data);
 };
@@ -101,6 +103,7 @@
                                   grpc_handshaker* handshaker,
                                   grpc_endpoint* endpoint,
                                   grpc_channel_args* args,
+                                  gpr_slice_buffer* read_buffer,
                                   gpr_timespec deadline,
                                   grpc_tcp_server_acceptor* acceptor,
                                   grpc_handshaker_done_cb cb, void* user_data);
diff --git a/src/core/lib/channel/http_client_filter.c b/src/core/lib/channel/http_client_filter.c
index 8057e25..1dc05fb 100644
--- a/src/core/lib/channel/http_client_filter.c
+++ b/src/core/lib/channel/http_client_filter.c
@@ -43,6 +43,9 @@
 #define EXPECTED_CONTENT_TYPE "application/grpc"
 #define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1
 
+/* default maximum size of payload eligable for GET request */
+static const size_t kMaxPayloadSizeForGet = 2048;
+
 typedef struct call_data {
   grpc_linked_mdelem method;
   grpc_linked_mdelem scheme;
@@ -50,20 +53,39 @@
   grpc_linked_mdelem te_trailers;
   grpc_linked_mdelem content_type;
   grpc_linked_mdelem user_agent;
+  grpc_linked_mdelem payload_bin;
 
   grpc_metadata_batch *recv_initial_metadata;
+  uint8_t *payload_bytes;
+
+  /* Vars to read data off of send_message */
+  grpc_transport_stream_op send_op;
+  uint32_t send_length;
+  uint32_t send_flags;
+  gpr_slice incoming_slice;
+  grpc_slice_buffer_stream replacement_stream;
+  gpr_slice_buffer slices;
+  /* flag that indicates that all slices of send_messages aren't availble */
+  bool send_message_blocked;
 
   /** Closure to call when finished with the hc_on_recv hook */
   grpc_closure *on_done_recv;
+  grpc_closure *on_complete;
+  grpc_closure *post_send;
+
   /** Receive closures are chained: we inject this closure as the on_done_recv
       up-call on transport_op, and remember to call our on_done_recv member
       after handling it. */
   grpc_closure hc_on_recv;
+  grpc_closure hc_on_complete;
+  grpc_closure got_slice;
+  grpc_closure send_done;
 } call_data;
 
 typedef struct channel_data {
   grpc_mdelem *static_scheme;
   grpc_mdelem *user_agent;
+  size_t max_payload_size_for_get;
 } channel_data;
 
 typedef struct {
@@ -81,8 +103,8 @@
                  grpc_mdstr_as_c_string(md->value));
     gpr_slice message = gpr_slice_from_copied_string(message_string);
     gpr_free(message_string);
-    grpc_call_element_send_cancel_with_message(a->exec_ctx, a->elem,
-                                               GRPC_STATUS_CANCELLED, &message);
+    grpc_call_element_send_close_with_message(a->exec_ctx, a->elem,
+                                              GRPC_STATUS_CANCELLED, &message);
     return NULL;
   } else if (md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) {
     return NULL;
@@ -119,6 +141,24 @@
   calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, error);
 }
 
+static void hc_on_complete(grpc_exec_ctx *exec_ctx, void *user_data,
+                           grpc_error *error) {
+  grpc_call_element *elem = user_data;
+  call_data *calld = elem->call_data;
+  if (calld->payload_bytes) {
+    gpr_free(calld->payload_bytes);
+    calld->payload_bytes = NULL;
+  }
+  calld->on_complete->cb(exec_ctx, calld->on_complete->cb_arg, error);
+}
+
+static void send_done(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
+  grpc_call_element *elem = elemp;
+  call_data *calld = elem->call_data;
+  gpr_slice_buffer_reset_and_unref(&calld->slices);
+  calld->post_send->cb(exec_ctx, calld->post_send->cb_arg, error);
+}
+
 static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
   /* eat the things we'd like to set ourselves */
   if (md->key == GRPC_MDSTR_METHOD) return NULL;
@@ -129,22 +169,104 @@
   return md;
 }
 
-static void hc_mutate_op(grpc_call_element *elem,
+static void continue_send_message(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem) {
+  call_data *calld = elem->call_data;
+  uint8_t *wrptr = calld->payload_bytes;
+  while (grpc_byte_stream_next(exec_ctx, calld->send_op.send_message,
+                               &calld->incoming_slice, ~(size_t)0,
+                               &calld->got_slice)) {
+    memcpy(wrptr, GPR_SLICE_START_PTR(calld->incoming_slice),
+           GPR_SLICE_LENGTH(calld->incoming_slice));
+    wrptr += GPR_SLICE_LENGTH(calld->incoming_slice);
+    gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
+    if (calld->send_length == calld->slices.length) {
+      calld->send_message_blocked = false;
+      break;
+    }
+  }
+}
+
+static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
+  grpc_call_element *elem = elemp;
+  call_data *calld = elem->call_data;
+  calld->send_message_blocked = false;
+  gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
+  if (calld->send_length == calld->slices.length) {
+    /* Pass down the original send_message op that was blocked.*/
+    grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices,
+                                  calld->send_flags);
+    calld->send_op.send_message = &calld->replacement_stream.base;
+    calld->post_send = calld->send_op.on_complete;
+    calld->send_op.on_complete = &calld->send_done;
+    grpc_call_next_op(exec_ctx, elem, &calld->send_op);
+  } else {
+    continue_send_message(exec_ctx, elem);
+  }
+}
+
+static void hc_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                          grpc_transport_stream_op *op) {
   /* grab pointers to our data from the call element */
   call_data *calld = elem->call_data;
   channel_data *channeld = elem->channel_data;
+
   if (op->send_initial_metadata != NULL) {
+    /* Decide which HTTP VERB to use. We use GET if the request is marked
+    cacheable, and the operation contains both initial metadata and send
+    message, and the payload is below the size threshold, and all the data
+    for this request is immediately available. */
+    grpc_mdelem *method = GRPC_MDELEM_METHOD_POST;
+    calld->send_message_blocked = false;
+    if ((op->send_initial_metadata_flags &
+         GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) &&
+        op->send_message != NULL &&
+        op->send_message->length < channeld->max_payload_size_for_get) {
+      method = GRPC_MDELEM_METHOD_GET;
+      calld->send_message_blocked = true;
+    } else if (op->send_initial_metadata_flags &
+               GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) {
+      method = GRPC_MDELEM_METHOD_PUT;
+    }
+
+    /* Attempt to read the data from send_message and create a header field. */
+    if (method == GRPC_MDELEM_METHOD_GET) {
+      /* allocate memory to hold the entire payload */
+      calld->payload_bytes = gpr_malloc(op->send_message->length);
+
+      /* read slices of send_message and copy into payload_bytes */
+      calld->send_op = *op;
+      calld->send_length = op->send_message->length;
+      calld->send_flags = op->send_message->flags;
+      continue_send_message(exec_ctx, elem);
+
+      if (calld->send_message_blocked == false) {
+        /* when all the send_message data is available, then create a MDELEM and
+        append to headers */
+        grpc_mdelem *payload_bin = grpc_mdelem_from_metadata_strings(
+            GRPC_MDSTR_GRPC_PAYLOAD_BIN,
+            grpc_mdstr_from_buffer(calld->payload_bytes,
+                                   op->send_message->length));
+        grpc_metadata_batch_add_tail(op->send_initial_metadata,
+                                     &calld->payload_bin, payload_bin);
+        calld->on_complete = op->on_complete;
+        op->on_complete = &calld->hc_on_complete;
+        op->send_message = NULL;
+      } else {
+        /* Not all data is available. Fall back to POST. */
+        gpr_log(GPR_DEBUG,
+                "Request is marked Cacheable but not all data is available.\
+                            Falling back to POST");
+        method = GRPC_MDELEM_METHOD_POST;
+      }
+    }
+
     grpc_metadata_batch_filter(op->send_initial_metadata, client_strip_filter,
                                elem);
     /* Send : prefixed headers, which have to be before any application
        layer headers. */
-    grpc_metadata_batch_add_head(
-        op->send_initial_metadata, &calld->method,
-        op->send_initial_metadata_flags &
-                GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST
-            ? GRPC_MDELEM_METHOD_PUT
-            : GRPC_MDELEM_METHOD_POST);
+    grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->method,
+                                 method);
     grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->scheme,
                                  channeld->static_scheme);
     grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->te_trailers,
@@ -169,22 +291,41 @@
                                   grpc_transport_stream_op *op) {
   GPR_TIMER_BEGIN("hc_start_transport_op", 0);
   GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
-  hc_mutate_op(elem, op);
+  hc_mutate_op(exec_ctx, elem, op);
   GPR_TIMER_END("hc_start_transport_op", 0);
-  grpc_call_next_op(exec_ctx, elem, op);
+  call_data *calld = elem->call_data;
+  if (op->send_message != NULL && calld->send_message_blocked) {
+    /* Don't forward the op. send_message contains slices that aren't ready
+    yet. The call will be forwarded by the op_complete of slice read call.
+    */
+  } else {
+    grpc_call_next_op(exec_ctx, elem, op);
+  }
 }
 
 /* Constructor for call_data */
-static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {
+static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  grpc_call_element_args *args) {
   call_data *calld = elem->call_data;
   calld->on_done_recv = NULL;
+  calld->on_complete = NULL;
+  calld->payload_bytes = NULL;
+  gpr_slice_buffer_init(&calld->slices);
   grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem);
+  grpc_closure_init(&calld->hc_on_complete, hc_on_complete, elem);
+  grpc_closure_init(&calld->got_slice, got_slice, elem);
+  grpc_closure_init(&calld->send_done, send_done, elem);
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {}
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {
+  call_data *calld = elem->call_data;
+  gpr_slice_buffer_destroy(&calld->slices);
+}
 
 static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
   unsigned i;
@@ -207,6 +348,22 @@
   return GRPC_MDELEM_SCHEME_HTTP;
 }
 
+static size_t max_payload_size_from_args(const grpc_channel_args *args) {
+  if (args != NULL) {
+    for (size_t i = 0; i < args->num_args; ++i) {
+      if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET)) {
+        if (args->args[i].type != GRPC_ARG_INTEGER) {
+          gpr_log(GPR_ERROR, "%s: must be an integer",
+                  GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET);
+        } else {
+          return (size_t)args->args[i].value.integer;
+        }
+      }
+    }
+  }
+  return kMaxPayloadSizeForGet;
+}
+
 static grpc_mdstr *user_agent_from_args(const grpc_channel_args *args,
                                         const char *transport_name) {
   gpr_strvec v;
@@ -230,8 +387,9 @@
     }
   }
 
-  gpr_asprintf(&tmp, "%sgrpc-c/%s (%s; %s)", is_first ? "" : " ",
-               grpc_version_string(), GPR_PLATFORM_STRING, transport_name);
+  gpr_asprintf(&tmp, "%sgrpc-c/%s (%s; %s; %s)", is_first ? "" : " ",
+               grpc_version_string(), GPR_PLATFORM_STRING, transport_name,
+               grpc_g_stands_for());
   is_first = 0;
   gpr_strvec_add(&v, tmp);
 
@@ -264,6 +422,8 @@
   GPR_ASSERT(!args->is_last);
   GPR_ASSERT(args->optional_transport != NULL);
   chand->static_scheme = scheme_from_args(args->channel_args);
+  chand->max_payload_size_for_get =
+      max_payload_size_from_args(args->channel_args);
   chand->user_agent = grpc_mdelem_from_metadata_strings(
       GRPC_MDSTR_USER_AGENT,
       user_agent_from_args(args->channel_args,
diff --git a/src/core/lib/channel/http_client_filter.h b/src/core/lib/channel/http_client_filter.h
index 4708117..9e6e106 100644
--- a/src/core/lib/channel/http_client_filter.h
+++ b/src/core/lib/channel/http_client_filter.h
@@ -41,4 +41,7 @@
 /* Channel arg to override the http2 :scheme header */
 #define GRPC_ARG_HTTP2_SCHEME "grpc.http2_scheme"
 
+/* Channel arg to determine maximum size of payload eligable for GET request */
+#define GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET "grpc.max_payload_size_for_get"
+
 #endif /* GRPC_CORE_LIB_CHANNEL_HTTP_CLIENT_FILTER_H */
diff --git a/src/core/lib/channel/http_server_filter.c b/src/core/lib/channel/http_server_filter.c
index d0beebd..0f2bf97 100644
--- a/src/core/lib/channel/http_server_filter.c
+++ b/src/core/lib/channel/http_server_filter.c
@@ -49,17 +49,32 @@
   uint8_t seen_scheme;
   uint8_t seen_te_trailers;
   uint8_t seen_authority;
+  uint8_t seen_payload_bin;
   grpc_linked_mdelem status;
   grpc_linked_mdelem content_type;
 
+  /* flag to ensure payload_bin is delivered only once */
+  uint8_t payload_bin_delivered;
+
   grpc_metadata_batch *recv_initial_metadata;
   bool *recv_idempotent_request;
+  bool *recv_cacheable_request;
   /** Closure to call when finished with the hs_on_recv hook */
   grpc_closure *on_done_recv;
+  /** Closure to call when we retrieve read message from the payload-bin header
+   */
+  grpc_closure *recv_message_ready;
+  grpc_closure *on_complete;
+  grpc_byte_stream **pp_recv_message;
+  gpr_slice_buffer read_slice_buffer;
+  grpc_slice_buffer_stream read_stream;
+
   /** Receive closures are chained: we inject this closure as the on_done_recv
       up-call on transport_op, and remember to call our on_done_recv member
       after handling it. */
   grpc_closure hs_on_recv;
+  grpc_closure hs_on_complete;
+  grpc_closure hs_recv_message_ready;
 } call_data;
 
 typedef struct channel_data { uint8_t unused; } channel_data;
@@ -76,16 +91,20 @@
 
   /* Check if it is one of the headers we care about. */
   if (md == GRPC_MDELEM_TE_TRAILERS || md == GRPC_MDELEM_METHOD_POST ||
-      md == GRPC_MDELEM_METHOD_PUT || md == GRPC_MDELEM_SCHEME_HTTP ||
-      md == GRPC_MDELEM_SCHEME_HTTPS ||
+      md == GRPC_MDELEM_METHOD_PUT || md == GRPC_MDELEM_METHOD_GET ||
+      md == GRPC_MDELEM_SCHEME_HTTP || md == GRPC_MDELEM_SCHEME_HTTPS ||
       md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) {
     /* swallow it */
     if (md == GRPC_MDELEM_METHOD_POST) {
       calld->seen_method = 1;
       *calld->recv_idempotent_request = false;
+      *calld->recv_cacheable_request = false;
     } else if (md == GRPC_MDELEM_METHOD_PUT) {
       calld->seen_method = 1;
       *calld->recv_idempotent_request = true;
+    } else if (md == GRPC_MDELEM_METHOD_GET) {
+      calld->seen_method = 1;
+      *calld->recv_cacheable_request = true;
     } else if (md->key == GRPC_MDSTR_SCHEME) {
       calld->seen_scheme = 1;
     } else if (md == GRPC_MDELEM_TE_TRAILERS) {
@@ -137,6 +156,16 @@
         GRPC_MDSTR_AUTHORITY, GRPC_MDSTR_REF(md->value));
     calld->seen_authority = 1;
     return authority;
+  } else if (md->key == GRPC_MDSTR_GRPC_PAYLOAD_BIN) {
+    /* Retrieve the payload from the value of the 'grpc-internal-payload-bin'
+       header field */
+    calld->seen_payload_bin = 1;
+    gpr_slice_buffer_init(&calld->read_slice_buffer);
+    gpr_slice_buffer_add(&calld->read_slice_buffer,
+                         gpr_slice_ref(md->value->slice));
+    grpc_slice_buffer_stream_init(&calld->read_stream,
+                                  &calld->read_slice_buffer, 0);
+    return NULL;
   } else {
     return md;
   }
@@ -189,6 +218,36 @@
   GRPC_ERROR_UNREF(err);
 }
 
+static void hs_on_complete(grpc_exec_ctx *exec_ctx, void *user_data,
+                           grpc_error *err) {
+  grpc_call_element *elem = user_data;
+  call_data *calld = elem->call_data;
+  /* Call recv_message_ready if we got the payload via the header field */
+  if (calld->seen_payload_bin && calld->recv_message_ready != NULL) {
+    *calld->pp_recv_message = calld->payload_bin_delivered
+                                  ? NULL
+                                  : (grpc_byte_stream *)&calld->read_stream;
+    calld->recv_message_ready->cb(exec_ctx, calld->recv_message_ready->cb_arg,
+                                  err);
+    calld->recv_message_ready = NULL;
+    calld->payload_bin_delivered = true;
+  }
+  calld->on_complete->cb(exec_ctx, calld->on_complete->cb_arg, err);
+}
+
+static void hs_recv_message_ready(grpc_exec_ctx *exec_ctx, void *user_data,
+                                  grpc_error *err) {
+  grpc_call_element *elem = user_data;
+  call_data *calld = elem->call_data;
+  if (calld->seen_payload_bin) {
+    /* do nothing. This is probably a GET request, and payload will be returned
+    in hs_on_complete callback. */
+  } else {
+    calld->recv_message_ready->cb(exec_ctx, calld->recv_message_ready->cb_arg,
+                                  err);
+  }
+}
+
 static void hs_mutate_op(grpc_call_element *elem,
                          grpc_transport_stream_op *op) {
   /* grab pointers to our data from the call element */
@@ -206,11 +265,25 @@
   if (op->recv_initial_metadata) {
     /* substitute our callback for the higher callback */
     GPR_ASSERT(op->recv_idempotent_request != NULL);
+    GPR_ASSERT(op->recv_cacheable_request != NULL);
     calld->recv_initial_metadata = op->recv_initial_metadata;
     calld->recv_idempotent_request = op->recv_idempotent_request;
+    calld->recv_cacheable_request = op->recv_cacheable_request;
     calld->on_done_recv = op->recv_initial_metadata_ready;
     op->recv_initial_metadata_ready = &calld->hs_on_recv;
   }
+
+  if (op->recv_message) {
+    calld->recv_message_ready = op->recv_message_ready;
+    calld->pp_recv_message = op->recv_message;
+    if (op->recv_message_ready) {
+      op->recv_message_ready = &calld->hs_recv_message_ready;
+    }
+    if (op->on_complete) {
+      calld->on_complete = op->on_complete;
+      op->on_complete = &calld->hs_on_complete;
+    }
+  }
 }
 
 static void hs_start_transport_op(grpc_exec_ctx *exec_ctx,
@@ -224,18 +297,23 @@
 }
 
 /* Constructor for call_data */
-static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {
+static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  grpc_call_element_args *args) {
   /* grab pointers to our data from the call element */
   call_data *calld = elem->call_data;
   /* initialize members */
   memset(calld, 0, sizeof(*calld));
   grpc_closure_init(&calld->hs_on_recv, hs_on_recv, elem);
+  grpc_closure_init(&calld->hs_on_complete, hs_on_complete, elem);
+  grpc_closure_init(&calld->hs_recv_message_ready, hs_recv_message_ready, elem);
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {}
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {}
 
 /* Constructor for channel_data */
 static void init_channel_elem(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/lib/channel/message_size_filter.c b/src/core/lib/channel/message_size_filter.c
new file mode 100644
index 0000000..02fc68f
--- /dev/null
+++ b/src/core/lib/channel/message_size_filter.c
@@ -0,0 +1,169 @@
+//
+// Copyright 2016, 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/lib/channel/message_size_filter.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/channel/channel_args.h"
+
+#define DEFAULT_MAX_SEND_MESSAGE_LENGTH -1  // Unlimited.
+// The protobuf library will (by default) start warning at 100 megs.
+#define DEFAULT_MAX_RECV_MESSAGE_LENGTH (4 * 1024 * 1024)
+
+typedef struct call_data {
+  // Receive closures are chained: we inject this closure as the
+  // recv_message_ready up-call on transport_stream_op, and remember to
+  // call our next_recv_message_ready member after handling it.
+  grpc_closure recv_message_ready;
+  // Used by recv_message_ready.
+  grpc_byte_stream** recv_message;
+  // Original recv_message_ready callback, invoked after our own.
+  grpc_closure* next_recv_message_ready;
+} call_data;
+
+typedef struct channel_data {
+  int max_send_size;
+  int max_recv_size;
+} channel_data;
+
+// Callback invoked when we receive a message.  Here we check the max
+// receive message size.
+static void recv_message_ready(grpc_exec_ctx* exec_ctx, void* user_data,
+                               grpc_error* error) {
+  grpc_call_element* elem = user_data;
+  call_data* calld = elem->call_data;
+  channel_data* chand = elem->channel_data;
+  if (*calld->recv_message != NULL && chand->max_recv_size >= 0 &&
+      (*calld->recv_message)->length > (size_t)chand->max_recv_size) {
+    char* message_string;
+    gpr_asprintf(&message_string,
+                 "Received message larger than max (%u vs. %d)",
+                 (*calld->recv_message)->length, chand->max_recv_size);
+    gpr_slice message = gpr_slice_from_copied_string(message_string);
+    gpr_free(message_string);
+    grpc_call_element_send_close_with_message(
+        exec_ctx, elem, GRPC_STATUS_INVALID_ARGUMENT, &message);
+  }
+  // Invoke the next callback.
+  grpc_exec_ctx_sched(exec_ctx, calld->next_recv_message_ready, error, NULL);
+}
+
+// Start transport op.
+static void start_transport_stream_op(grpc_exec_ctx* exec_ctx,
+                                      grpc_call_element* elem,
+                                      grpc_transport_stream_op* op) {
+  call_data* calld = elem->call_data;
+  channel_data* chand = elem->channel_data;
+  // Check max send message size.
+  if (op->send_message != NULL && chand->max_send_size >= 0 &&
+      op->send_message->length > (size_t)chand->max_send_size) {
+    char* message_string;
+    gpr_asprintf(&message_string, "Sent message larger than max (%u vs. %d)",
+                 op->send_message->length, chand->max_send_size);
+    gpr_slice message = gpr_slice_from_copied_string(message_string);
+    gpr_free(message_string);
+    grpc_call_element_send_close_with_message(
+        exec_ctx, elem, GRPC_STATUS_INVALID_ARGUMENT, &message);
+  }
+  // Inject callback for receiving a message.
+  if (op->recv_message_ready != NULL) {
+    calld->next_recv_message_ready = op->recv_message_ready;
+    calld->recv_message = op->recv_message;
+    op->recv_message_ready = &calld->recv_message_ready;
+  }
+  // Chain to the next filter.
+  grpc_call_next_op(exec_ctx, elem, op);
+}
+
+// Constructor for call_data.
+static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx,
+                                  grpc_call_element* elem,
+                                  grpc_call_element_args* args) {
+  call_data* calld = elem->call_data;
+  calld->next_recv_message_ready = NULL;
+  grpc_closure_init(&calld->recv_message_ready, recv_message_ready, elem);
+  return GRPC_ERROR_NONE;
+}
+
+// Destructor for call_data.
+static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              void* ignored) {}
+
+// Constructor for channel_data.
+static void init_channel_elem(grpc_exec_ctx* exec_ctx,
+                              grpc_channel_element* elem,
+                              grpc_channel_element_args* args) {
+  GPR_ASSERT(!args->is_last);
+  channel_data* chand = elem->channel_data;
+  memset(chand, 0, sizeof(*chand));
+  chand->max_send_size = DEFAULT_MAX_SEND_MESSAGE_LENGTH;
+  chand->max_recv_size = DEFAULT_MAX_RECV_MESSAGE_LENGTH;
+  for (size_t i = 0; i < args->channel_args->num_args; ++i) {
+    if (strcmp(args->channel_args->args[i].key,
+               GRPC_ARG_MAX_SEND_MESSAGE_LENGTH) == 0) {
+      const grpc_integer_options options = {DEFAULT_MAX_SEND_MESSAGE_LENGTH, 0,
+                                            INT_MAX};
+      chand->max_send_size =
+          grpc_channel_arg_get_integer(&args->channel_args->args[i], options);
+    }
+    if (strcmp(args->channel_args->args[i].key,
+               GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH) == 0) {
+      const grpc_integer_options options = {DEFAULT_MAX_RECV_MESSAGE_LENGTH, 0,
+                                            INT_MAX};
+      chand->max_recv_size =
+          grpc_channel_arg_get_integer(&args->channel_args->args[i], options);
+    }
+  }
+}
+
+// Destructor for channel_data.
+static void destroy_channel_elem(grpc_exec_ctx* exec_ctx,
+                                 grpc_channel_element* elem) {}
+
+const grpc_channel_filter grpc_message_size_filter = {
+    start_transport_stream_op,
+    grpc_channel_next_op,
+    sizeof(call_data),
+    init_call_elem,
+    grpc_call_stack_ignore_set_pollset_or_pollset_set,
+    destroy_call_elem,
+    sizeof(channel_data),
+    init_channel_elem,
+    destroy_channel_elem,
+    grpc_call_next_get_peer,
+    "message_size"};
diff --git a/src/core/lib/channel/message_size_filter.h b/src/core/lib/channel/message_size_filter.h
new file mode 100644
index 0000000..a88ff7f
--- /dev/null
+++ b/src/core/lib/channel/message_size_filter.h
@@ -0,0 +1,39 @@
+//
+// Copyright 2016, 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_CORE_LIB_CHANNEL_MESSAGE_SIZE_FILTER_H
+#define GRPC_CORE_LIB_CHANNEL_MESSAGE_SIZE_FILTER_H
+
+#include "src/core/lib/channel/channel_stack.h"
+
+extern const grpc_channel_filter grpc_message_size_filter;
+
+#endif /* GRPC_CORE_LIB_CHANNEL_MESSAGE_SIZE_FILTER_H */
diff --git a/src/core/lib/http/format_request.c b/src/core/lib/http/format_request.c
index 9240356..e818b70 100644
--- a/src/core/lib/http/format_request.c
+++ b/src/core/lib/http/format_request.c
@@ -44,7 +44,7 @@
 #include "src/core/lib/support/string.h"
 
 static void fill_common_header(const grpc_httpcli_request *request,
-                               gpr_strvec *buf) {
+                               gpr_strvec *buf, bool connection_close) {
   size_t i;
   gpr_strvec_add(buf, gpr_strdup(request->http.path));
   gpr_strvec_add(buf, gpr_strdup(" HTTP/1.0\r\n"));
@@ -52,7 +52,8 @@
   gpr_strvec_add(buf, gpr_strdup("Host: "));
   gpr_strvec_add(buf, gpr_strdup(request->host));
   gpr_strvec_add(buf, gpr_strdup("\r\n"));
-  gpr_strvec_add(buf, gpr_strdup("Connection: close\r\n"));
+  if (connection_close)
+    gpr_strvec_add(buf, gpr_strdup("Connection: close\r\n"));
   gpr_strvec_add(buf,
                  gpr_strdup("User-Agent: " GRPC_HTTPCLI_USER_AGENT "\r\n"));
   /* user supplied headers */
@@ -71,7 +72,7 @@
 
   gpr_strvec_init(&out);
   gpr_strvec_add(&out, gpr_strdup("GET "));
-  fill_common_header(request, &out);
+  fill_common_header(request, &out, true);
   gpr_strvec_add(&out, gpr_strdup("\r\n"));
 
   flat = gpr_strvec_flatten(&out, &flat_len);
@@ -91,7 +92,7 @@
   gpr_strvec_init(&out);
 
   gpr_strvec_add(&out, gpr_strdup("POST "));
-  fill_common_header(request, &out);
+  fill_common_header(request, &out, true);
   if (body_bytes) {
     uint8_t has_content_type = 0;
     for (i = 0; i < request->http.hdr_count; i++) {
@@ -118,3 +119,16 @@
 
   return gpr_slice_new(tmp, out_len, gpr_free);
 }
+
+gpr_slice grpc_httpcli_format_connect_request(
+    const grpc_httpcli_request *request) {
+  gpr_strvec out;
+  gpr_strvec_init(&out);
+  gpr_strvec_add(&out, gpr_strdup("CONNECT "));
+  fill_common_header(request, &out, false);
+  gpr_strvec_add(&out, gpr_strdup("\r\n"));
+  size_t flat_len;
+  char *flat = gpr_strvec_flatten(&out, &flat_len);
+  gpr_strvec_destroy(&out);
+  return gpr_slice_new(flat, flat_len, gpr_free);
+}
diff --git a/src/core/lib/http/format_request.h b/src/core/lib/http/format_request.h
index 1543efe..7abd55f 100644
--- a/src/core/lib/http/format_request.h
+++ b/src/core/lib/http/format_request.h
@@ -41,5 +41,7 @@
 gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request,
                                            const char *body_bytes,
                                            size_t body_size);
+gpr_slice grpc_httpcli_format_connect_request(
+    const grpc_httpcli_request *request);
 
 #endif /* GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H */
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c
index 18135bc..7f3c2d1 100644
--- a/src/core/lib/http/httpcli.c
+++ b/src/core/lib/http/httpcli.c
@@ -146,7 +146,7 @@
     if (GPR_SLICE_LENGTH(req->incoming.slices[i])) {
       req->have_read_byte = 1;
       grpc_error *err =
-          grpc_http_parser_parse(&req->parser, req->incoming.slices[i]);
+          grpc_http_parser_parse(&req->parser, req->incoming.slices[i], NULL);
       if (err != GRPC_ERROR_NONE) {
         finish(exec_ctx, req, err);
         return;
diff --git a/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h
index 662e176..320c0f8 100644
--- a/src/core/lib/http/httpcli.h
+++ b/src/core/lib/http/httpcli.h
@@ -93,8 +93,7 @@
    'request' contains request parameters - these are caller owned and can be
      destroyed once the call returns
    'deadline' contains a deadline for the request (or gpr_inf_future)
-   'on_response' is a callback to report results to (and 'user_data' is a user
-     supplied pointer to pass to said call) */
+   'on_response' is a callback to report results to */
 void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
                       grpc_polling_entity *pollent,
                       const grpc_httpcli_request *request,
@@ -113,8 +112,7 @@
    'deadline' contains a deadline for the request (or gpr_inf_future)
    'em' points to a caller owned event manager that must be alive for the
      lifetime of the request
-   'on_response' is a callback to report results to (and 'user_data' is a user
-     supplied pointer to pass to said call)
+   'on_response' is a callback to report results to
    Does not support ?var1=val1&var2=val2 in the path. */
 void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
                        grpc_polling_entity *pollent,
diff --git a/src/core/lib/http/httpcli_security_connector.c b/src/core/lib/http/httpcli_security_connector.c
index a57d93b..0006e80 100644
--- a/src/core/lib/http/httpcli_security_connector.c
+++ b/src/core/lib/http/httpcli_security_connector.c
@@ -61,6 +61,7 @@
 static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx,
                                      grpc_channel_security_connector *sc,
                                      grpc_endpoint *nonsecure_endpoint,
+                                     gpr_slice_buffer *read_buffer,
                                      gpr_timespec deadline,
                                      grpc_security_handshake_done_cb cb,
                                      void *user_data) {
@@ -69,6 +70,7 @@
   tsi_result result = TSI_OK;
   tsi_handshaker *handshaker;
   if (c->handshaker_factory == NULL) {
+    gpr_free(read_buffer);
     cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
     return;
   }
@@ -77,10 +79,12 @@
   if (result != TSI_OK) {
     gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
             tsi_result_to_string(result));
+    gpr_free(read_buffer);
     cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
   } else {
     grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, true,
-                               nonsecure_endpoint, deadline, cb, user_data);
+                               nonsecure_endpoint, read_buffer, deadline, cb,
+                               user_data);
   }
 }
 
@@ -183,7 +187,7 @@
                  pem_root_certs, pem_root_certs_size, host, &sc) ==
              GRPC_SECURITY_OK);
   grpc_channel_security_connector_do_handshake(
-      exec_ctx, sc, tcp, deadline, on_secure_transport_setup_done, c);
+      exec_ctx, sc, tcp, NULL, deadline, on_secure_transport_setup_done, c);
   GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "httpcli");
 }
 
diff --git a/src/core/lib/http/parser.c b/src/core/lib/http/parser.c
index 92ed08a..be9e9b6 100644
--- a/src/core/lib/http/parser.c
+++ b/src/core/lib/http/parser.c
@@ -33,6 +33,7 @@
 
 #include "src/core/lib/http/parser.h"
 
+#include <stdbool.h>
 #include <string.h>
 
 #include <grpc/support/alloc.h>
@@ -200,7 +201,8 @@
   return error;
 }
 
-static grpc_error *finish_line(grpc_http_parser *parser) {
+static grpc_error *finish_line(grpc_http_parser *parser,
+                               bool *found_body_start) {
   grpc_error *err;
   switch (parser->state) {
     case GRPC_HTTP_FIRST_LINE:
@@ -211,6 +213,7 @@
     case GRPC_HTTP_HEADERS:
       if (parser->cur_line_length == parser->cur_line_end_length) {
         parser->state = GRPC_HTTP_BODY;
+        *found_body_start = true;
         break;
       }
       err = add_header(parser);
@@ -274,7 +277,8 @@
   return false;
 }
 
-static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte) {
+static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte,
+                           bool *found_body_start) {
   switch (parser->state) {
     case GRPC_HTTP_FIRST_LINE:
     case GRPC_HTTP_HEADERS:
@@ -282,20 +286,18 @@
         if (grpc_http1_trace)
           gpr_log(GPR_ERROR, "HTTP client max line length (%d) exceeded",
                   GRPC_HTTP_PARSER_MAX_HEADER_LENGTH);
-        return 0;
+        return GRPC_ERROR_NONE;
       }
       parser->cur_line[parser->cur_line_length] = byte;
       parser->cur_line_length++;
       if (check_line(parser)) {
-        return finish_line(parser);
-      } else {
-        return GRPC_ERROR_NONE;
+        return finish_line(parser, found_body_start);
       }
-      GPR_UNREACHABLE_CODE(return 0);
+      return GRPC_ERROR_NONE;
     case GRPC_HTTP_BODY:
       return addbyte_body(parser, byte);
   }
-  GPR_UNREACHABLE_CODE(return 0);
+  GPR_UNREACHABLE_CODE(return GRPC_ERROR_NONE);
 }
 
 void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type,
@@ -331,14 +333,15 @@
   gpr_free(response->hdrs);
 }
 
-grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) {
-  size_t i;
-
-  for (i = 0; i < GPR_SLICE_LENGTH(slice); i++) {
-    grpc_error *err = addbyte(parser, GPR_SLICE_START_PTR(slice)[i]);
+grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice,
+                                   size_t *start_of_body) {
+  for (size_t i = 0; i < GPR_SLICE_LENGTH(slice); i++) {
+    bool found_body_start = false;
+    grpc_error *err =
+        addbyte(parser, GPR_SLICE_START_PTR(slice)[i], &found_body_start);
     if (err != GRPC_ERROR_NONE) return err;
+    if (found_body_start && start_of_body != NULL) *start_of_body = i + 1;
   }
-
   return GRPC_ERROR_NONE;
 }
 
diff --git a/src/core/lib/http/parser.h b/src/core/lib/http/parser.h
index 6df3cc8..fab4297 100644
--- a/src/core/lib/http/parser.h
+++ b/src/core/lib/http/parser.h
@@ -113,7 +113,9 @@
                            void *request_or_response);
 void grpc_http_parser_destroy(grpc_http_parser *parser);
 
-grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice);
+/* Sets \a start_of_body to the offset in \a slice of the start of the body. */
+grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice,
+                                   size_t *start_of_body);
 grpc_error *grpc_http_parser_eof(grpc_http_parser *parser);
 
 void grpc_http_request_destroy(grpc_http_request *request);
diff --git a/src/core/lib/iomgr/closure.c b/src/core/lib/iomgr/closure.c
index 0b6c3b2..1ba0a5c 100644
--- a/src/core/lib/iomgr/closure.c
+++ b/src/core/lib/iomgr/closure.c
@@ -41,6 +41,10 @@
   closure->cb_arg = cb_arg;
 }
 
+void grpc_closure_list_init(grpc_closure_list *closure_list) {
+  closure_list->head = closure_list->tail = NULL;
+}
+
 void grpc_closure_list_append(grpc_closure_list *closure_list,
                               grpc_closure *closure, grpc_error *error) {
   if (closure == NULL) {
diff --git a/src/core/lib/iomgr/closure.h b/src/core/lib/iomgr/closure.h
index 08e59a1..c1a22b6 100644
--- a/src/core/lib/iomgr/closure.h
+++ b/src/core/lib/iomgr/closure.h
@@ -37,6 +37,7 @@
 #include <grpc/support/port_platform.h>
 #include <stdbool.h>
 #include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/support/mpscq.h"
 
 struct grpc_closure;
 typedef struct grpc_closure grpc_closure;
@@ -60,6 +61,14 @@
 
 /** A closure over a grpc_iomgr_cb_func. */
 struct grpc_closure {
+  /** Once queued, next indicates the next queued closure; before then, scratch
+   *  space */
+  union {
+    grpc_closure *next;
+    gpr_mpscq_node atm_next;
+    uintptr_t scratch;
+  } next_data;
+
   /** Bound callback. */
   grpc_iomgr_cb_func cb;
 
@@ -68,13 +77,6 @@
 
   /** Once queued, the result of the closure. Before then: scratch space */
   grpc_error *error;
-
-  /** Once queued, next indicates the next queued closure; before then, scratch
-   *  space */
-  union {
-    grpc_closure *next;
-    uintptr_t scratch;
-  } next_data;
 };
 
 /** Initializes \a closure with \a cb and \a cb_arg. */
@@ -87,6 +89,8 @@
 #define GRPC_CLOSURE_LIST_INIT \
   { NULL, NULL }
 
+void grpc_closure_list_init(grpc_closure_list *list);
+
 /** add \a closure to the end of \a list
     and set \a closure's result to \a error */
 void grpc_closure_list_append(grpc_closure_list *list, grpc_closure *closure,
diff --git a/src/core/lib/iomgr/combiner.c b/src/core/lib/iomgr/combiner.c
new file mode 100644
index 0000000..831bdb4
--- /dev/null
+++ b/src/core/lib/iomgr/combiner.c
@@ -0,0 +1,293 @@
+/*
+ *
+ * Copyright 2016, 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/lib/iomgr/combiner.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/workqueue.h"
+#include "src/core/lib/profiling/timers.h"
+
+int grpc_combiner_trace = 0;
+
+#define GRPC_COMBINER_TRACE(fn) \
+  do {                          \
+    if (grpc_combiner_trace) {  \
+      fn;                       \
+    }                           \
+  } while (0)
+
+struct grpc_combiner {
+  grpc_workqueue *optional_workqueue;
+  gpr_mpscq queue;
+  // state is:
+  // lower bit - zero if orphaned
+  // other bits - number of items queued on the lock
+  gpr_atm state;
+  bool take_async_break_before_final_list;
+  grpc_closure_list final_list;
+  grpc_closure continue_finishing;
+};
+
+grpc_combiner *grpc_combiner_create(grpc_workqueue *optional_workqueue) {
+  grpc_combiner *lock = gpr_malloc(sizeof(*lock));
+  lock->optional_workqueue = optional_workqueue;
+  gpr_atm_no_barrier_store(&lock->state, 1);
+  gpr_mpscq_init(&lock->queue);
+  lock->take_async_break_before_final_list = false;
+  grpc_closure_list_init(&lock->final_list);
+  GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, "C:%p create", lock));
+  return lock;
+}
+
+static void really_destroy(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) {
+  GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, "C:%p really_destroy", lock));
+  GPR_ASSERT(gpr_atm_no_barrier_load(&lock->state) == 0);
+  gpr_mpscq_destroy(&lock->queue);
+  GRPC_WORKQUEUE_UNREF(exec_ctx, lock->optional_workqueue, "combiner");
+  gpr_free(lock);
+}
+
+void grpc_combiner_destroy(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) {
+  gpr_atm old_state = gpr_atm_full_fetch_add(&lock->state, -1);
+  GRPC_COMBINER_TRACE(gpr_log(
+      GPR_DEBUG, "C:%p really_destroy old_state=%" PRIdPTR, lock, old_state));
+  if (old_state == 1) {
+    really_destroy(exec_ctx, lock);
+  }
+}
+
+static bool maybe_finish_one(grpc_exec_ctx *exec_ctx, grpc_combiner *lock);
+static void finish(grpc_exec_ctx *exec_ctx, grpc_combiner *lock);
+
+static void continue_finishing_mainline(grpc_exec_ctx *exec_ctx, void *arg,
+                                        grpc_error *error) {
+  GPR_TIMER_BEGIN("combiner.continue_executing_mainline", 0);
+  grpc_combiner *lock = arg;
+  GRPC_COMBINER_TRACE(
+      gpr_log(GPR_DEBUG, "C:%p continue_finishing_mainline", lock));
+  GPR_ASSERT(exec_ctx->active_combiner == NULL);
+  exec_ctx->active_combiner = lock;
+  if (maybe_finish_one(exec_ctx, lock)) finish(exec_ctx, lock);
+  GPR_ASSERT(exec_ctx->active_combiner == lock);
+  exec_ctx->active_combiner = NULL;
+  GPR_TIMER_END("combiner.continue_executing_mainline", 0);
+}
+
+static void execute_final(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) {
+  GPR_TIMER_BEGIN("combiner.execute_final", 0);
+  grpc_closure *c = lock->final_list.head;
+  GPR_ASSERT(c != NULL);
+  grpc_closure_list_init(&lock->final_list);
+  lock->take_async_break_before_final_list = false;
+  int loops = 0;
+  while (c != NULL) {
+    GRPC_COMBINER_TRACE(
+        gpr_log(GPR_DEBUG, "C:%p execute_final[%d] c=%p", lock, loops, c));
+    grpc_closure *next = c->next_data.next;
+    grpc_error *error = c->error;
+    c->cb(exec_ctx, c->cb_arg, error);
+    GRPC_ERROR_UNREF(error);
+    c = next;
+    loops++;
+  }
+  GPR_TIMER_END("combiner.execute_final", 0);
+}
+
+static void continue_executing_final(grpc_exec_ctx *exec_ctx, void *arg,
+                                     grpc_error *error) {
+  GPR_TIMER_BEGIN("combiner.continue_executing_final", 0);
+  grpc_combiner *lock = arg;
+  GRPC_COMBINER_TRACE(
+      gpr_log(GPR_DEBUG, "C:%p continue_executing_final", lock));
+  GPR_ASSERT(exec_ctx->active_combiner == NULL);
+  exec_ctx->active_combiner = lock;
+  // quick peek to see if new things have turned up on the queue: if so, go back
+  // to executing them before the final list
+  if ((gpr_atm_acq_load(&lock->state) >> 1) > 1) {
+    if (maybe_finish_one(exec_ctx, lock)) finish(exec_ctx, lock);
+  } else {
+    execute_final(exec_ctx, lock);
+    finish(exec_ctx, lock);
+  }
+  GPR_ASSERT(exec_ctx->active_combiner == lock);
+  exec_ctx->active_combiner = NULL;
+  GPR_TIMER_END("combiner.continue_executing_final", 0);
+}
+
+static bool start_execute_final(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) {
+  GPR_TIMER_BEGIN("combiner.start_execute_final", 0);
+  GPR_ASSERT(exec_ctx->active_combiner == lock);
+  GRPC_COMBINER_TRACE(
+      gpr_log(GPR_DEBUG,
+              "C:%p start_execute_final take_async_break_before_final_list=%d",
+              lock, lock->take_async_break_before_final_list));
+  if (lock->take_async_break_before_final_list) {
+    grpc_closure_init(&lock->continue_finishing, continue_executing_final,
+                      lock);
+    grpc_exec_ctx_sched(exec_ctx, &lock->continue_finishing, GRPC_ERROR_NONE,
+                        GRPC_WORKQUEUE_REF(lock->optional_workqueue, "sched"));
+    GPR_TIMER_END("combiner.start_execute_final", 0);
+    return false;
+  } else {
+    execute_final(exec_ctx, lock);
+    GPR_TIMER_END("combiner.start_execute_final", 0);
+    return true;
+  }
+}
+
+static bool maybe_finish_one(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) {
+  GPR_TIMER_BEGIN("combiner.maybe_finish_one", 0);
+  gpr_mpscq_node *n = gpr_mpscq_pop(&lock->queue);
+  GRPC_COMBINER_TRACE(
+      gpr_log(GPR_DEBUG, "C:%p maybe_finish_one n=%p", lock, n));
+  GPR_ASSERT(exec_ctx->active_combiner == lock);
+  if (n == NULL) {
+    // Queue is in an transiently inconsistent state: a new item is being queued
+    // but is not visible to this thread yet.
+    // Use this as a cue that we should go off and do something else for a while
+    // (and come back later)
+    grpc_closure_init(&lock->continue_finishing, continue_finishing_mainline,
+                      lock);
+    grpc_exec_ctx_sched(exec_ctx, &lock->continue_finishing, GRPC_ERROR_NONE,
+                        GRPC_WORKQUEUE_REF(lock->optional_workqueue, "sched"));
+    GPR_TIMER_END("combiner.maybe_finish_one", 0);
+    return false;
+  }
+  grpc_closure *cl = (grpc_closure *)n;
+  grpc_error *error = cl->error;
+  cl->cb(exec_ctx, cl->cb_arg, error);
+  GRPC_ERROR_UNREF(error);
+  GPR_TIMER_END("combiner.maybe_finish_one", 0);
+  return true;
+}
+
+static void finish(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) {
+  bool (*executor)(grpc_exec_ctx * exec_ctx, grpc_combiner * lock);
+  GPR_TIMER_BEGIN("combiner.finish", 0);
+  int loops = 0;
+  do {
+    executor = maybe_finish_one;
+    gpr_atm old_state = gpr_atm_full_fetch_add(&lock->state, -2);
+    GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG,
+                                "C:%p finish[%d] old_state=%" PRIdPTR, lock,
+                                loops, old_state));
+    switch (old_state) {
+      default:
+        // we have multiple queued work items: just continue executing them
+        break;
+      case 5:  // we're down to one queued item: if it's the final list we
+      case 4:  // should do that
+        if (!grpc_closure_list_empty(lock->final_list)) {
+          executor = start_execute_final;
+        }
+        break;
+      case 3:  // had one count, one unorphaned --> unlocked unorphaned
+        GPR_TIMER_END("combiner.finish", 0);
+        return;
+      case 2:  // and one count, one orphaned --> unlocked and orphaned
+        really_destroy(exec_ctx, lock);
+        GPR_TIMER_END("combiner.finish", 0);
+        return;
+      case 1:
+      case 0:
+        // these values are illegal - representing an already unlocked or
+        // deleted lock
+        GPR_UNREACHABLE_CODE(return );
+    }
+    loops++;
+  } while (executor(exec_ctx, lock));
+  GPR_TIMER_END("combiner.finish", 0);
+}
+
+void grpc_combiner_execute(grpc_exec_ctx *exec_ctx, grpc_combiner *lock,
+                           grpc_closure *cl, grpc_error *error) {
+  GRPC_COMBINER_TRACE(
+      gpr_log(GPR_DEBUG, "C:%p grpc_combiner_execute c=%p", lock, cl));
+  GPR_TIMER_BEGIN("combiner.execute", 0);
+  gpr_atm last = gpr_atm_full_fetch_add(&lock->state, 2);
+  GPR_ASSERT(last & 1);  // ensure lock has not been destroyed
+  if (last == 1) {
+    exec_ctx->active_combiner = lock;
+    GPR_TIMER_BEGIN("combiner.execute_first_cb", 0);
+    cl->cb(exec_ctx, cl->cb_arg, error);
+    GPR_TIMER_END("combiner.execute_first_cb", 0);
+    GRPC_ERROR_UNREF(error);
+    finish(exec_ctx, lock);
+    GPR_ASSERT(exec_ctx->active_combiner == lock);
+    exec_ctx->active_combiner = NULL;
+  } else {
+    cl->error = error;
+    gpr_mpscq_push(&lock->queue, &cl->next_data.atm_next);
+  }
+  GPR_TIMER_END("combiner.execute", 0);
+}
+
+static void enqueue_finally(grpc_exec_ctx *exec_ctx, void *closure,
+                            grpc_error *error) {
+  grpc_combiner_execute_finally(exec_ctx, exec_ctx->active_combiner, closure,
+                                GRPC_ERROR_REF(error), false);
+}
+
+void grpc_combiner_execute_finally(grpc_exec_ctx *exec_ctx, grpc_combiner *lock,
+                                   grpc_closure *closure, grpc_error *error,
+                                   bool force_async_break) {
+  GRPC_COMBINER_TRACE(gpr_log(
+      GPR_DEBUG,
+      "C:%p grpc_combiner_execute_finally c=%p force_async_break=%d; ac=%p",
+      lock, closure, force_async_break, exec_ctx->active_combiner));
+  GPR_TIMER_BEGIN("combiner.execute_finally", 0);
+  if (exec_ctx->active_combiner != lock) {
+    GPR_TIMER_MARK("slowpath", 0);
+    grpc_combiner_execute(exec_ctx, lock,
+                          grpc_closure_create(enqueue_finally, closure), error);
+    GPR_TIMER_END("combiner.execute_finally", 0);
+    return;
+  }
+
+  if (force_async_break) {
+    lock->take_async_break_before_final_list = true;
+  }
+  if (grpc_closure_list_empty(lock->final_list)) {
+    gpr_atm_full_fetch_add(&lock->state, 2);
+  }
+  grpc_closure_list_append(&lock->final_list, closure, error);
+  GPR_TIMER_END("combiner.execute_finally", 0);
+}
+
+void grpc_combiner_force_async_finally(grpc_combiner *lock) {
+  lock->take_async_break_before_final_list = true;
+}
diff --git a/src/core/lib/iomgr/combiner.h b/src/core/lib/iomgr/combiner.h
new file mode 100644
index 0000000..1409db2
--- /dev/null
+++ b/src/core/lib/iomgr/combiner.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright 2016, 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_CORE_LIB_IOMGR_COMBINER_H
+#define GRPC_CORE_LIB_IOMGR_COMBINER_H
+
+#include <stddef.h>
+
+#include <grpc/support/atm.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/support/mpscq.h"
+
+// Provides serialized access to some resource.
+// Each action queued on a combiner is executed serially in a borrowed thread.
+// The actual thread executing actions may change over time (but there will only
+// every be one at a time).
+
+// Initialize the lock, with an optional workqueue to shift load to when
+// necessary
+grpc_combiner *grpc_combiner_create(grpc_workqueue *optional_workqueue);
+// Destroy the lock
+void grpc_combiner_destroy(grpc_exec_ctx *exec_ctx, grpc_combiner *lock);
+// Execute \a action within the lock.
+void grpc_combiner_execute(grpc_exec_ctx *exec_ctx, grpc_combiner *lock,
+                           grpc_closure *closure, grpc_error *error);
+// Execute \a action within the lock just prior to unlocking.
+// if \a hint_async_break is true, the combiner tries to hand execution to
+// another thread before finishing the primary queue of combined closures and
+// executing the finally list.
+// Deprecation warning: \a hint_async_break will be removed in a future version
+// Takes a very slow and round-about path if not called from a
+// grpc_combiner_execute closure.
+void grpc_combiner_execute_finally(grpc_exec_ctx *exec_ctx, grpc_combiner *lock,
+                                   grpc_closure *closure, grpc_error *error,
+                                   bool hint_async_break);
+// Deprecated: force the finally list execution onto another thread
+void grpc_combiner_force_async_finally(grpc_combiner *lock);
+
+extern int grpc_combiner_trace;
+
+#endif /* GRPC_CORE_LIB_IOMGR_COMBINER_H */
diff --git a/src/core/lib/iomgr/endpoint.h b/src/core/lib/iomgr/endpoint.h
index 894efc0..910a6f6 100644
--- a/src/core/lib/iomgr/endpoint.h
+++ b/src/core/lib/iomgr/endpoint.h
@@ -64,7 +64,8 @@
 /* When data is available on the connection, calls the callback with slices.
    Callback success indicates that the endpoint can accept more reads, failure
    indicates the endpoint is closed.
-   Valid slices may be placed into \a slices even on callback success == 0. */
+   Valid slices may be placed into \a slices even when the callback is
+   invoked with error != GRPC_ERROR_NONE. */
 void grpc_endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
                         gpr_slice_buffer *slices, grpc_closure *cb);
 
diff --git a/src/core/lib/iomgr/error.c b/src/core/lib/iomgr/error.c
index 149c556..31c8026 100644
--- a/src/core/lib/iomgr/error.c
+++ b/src/core/lib/iomgr/error.c
@@ -324,6 +324,64 @@
   return gpr_avl_get(err->strs, (void *)(uintptr_t)which);
 }
 
+typedef struct {
+  grpc_error *error;
+  grpc_status_code code;
+  const char *msg;
+} special_error_status_map;
+static special_error_status_map error_status_map[] = {
+    {GRPC_ERROR_NONE, GRPC_STATUS_OK, ""},
+    {GRPC_ERROR_CANCELLED, GRPC_STATUS_CANCELLED, "RPC cancelled"},
+    {GRPC_ERROR_OOM, GRPC_STATUS_RESOURCE_EXHAUSTED, "Out of memory"},
+};
+
+static grpc_error *recursively_find_error_with_status(grpc_error *error,
+                                                      intptr_t *status) {
+  // If the error itself has a status code, return it.
+  if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, status)) {
+    return error;
+  }
+  // Otherwise, search through its children.
+  intptr_t key = 0;
+  while (true) {
+    grpc_error *child_error = gpr_avl_get(error->errs, (void *)key++);
+    if (child_error == NULL) break;
+    grpc_error *result =
+        recursively_find_error_with_status(child_error, status);
+    if (result != NULL) return result;
+  }
+  return NULL;
+}
+
+void grpc_error_get_status(grpc_error *error, grpc_status_code *code,
+                           const char **msg) {
+  // Handle special errors via the static map.
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(error_status_map); ++i) {
+    if (error == error_status_map[i].error) {
+      *code = error_status_map[i].code;
+      *msg = error_status_map[i].msg;
+      return;
+    }
+  }
+  // Populate code.
+  // Start with the parent error and recurse through the tree of children
+  // until we find the first one that has a status code.
+  intptr_t status = GRPC_STATUS_UNKNOWN;  // Default in case we don't find one.
+  grpc_error *found_error = recursively_find_error_with_status(error, &status);
+  *code = (grpc_status_code)status;
+  // Now populate msg.
+  // If we found an error with a status code above, use that; otherwise,
+  // fall back to using the parent error.
+  if (found_error == NULL) found_error = error;
+  // If the error has a status message, use it.  Otherwise, fall back to
+  // the error description.
+  *msg = grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE);
+  if (*msg == NULL) {
+    *msg = grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION);
+    if (*msg == NULL) *msg = "uknown error";  // Just in case.
+  }
+}
+
 grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child) {
   GPR_TIMER_BEGIN("grpc_error_add_child", 0);
   grpc_error *new = copy_error_and_unref(src);
@@ -332,7 +390,7 @@
   return new;
 }
 
-static const char *no_error_string = "null";
+static const char *no_error_string = "\"No Error\"";
 static const char *oom_error_string = "\"Out of memory\"";
 static const char *cancelled_error_string = "\"Cancelled\"";
 
diff --git a/src/core/lib/iomgr/error.h b/src/core/lib/iomgr/error.h
index 13f898e..7e2fd7a 100644
--- a/src/core/lib/iomgr/error.h
+++ b/src/core/lib/iomgr/error.h
@@ -37,6 +37,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 
+#include <grpc/status.h>
 #include <grpc/support/time.h>
 
 /// Opaque representation of an error.
@@ -47,7 +48,8 @@
 ///  if a grpc_error is passed to a grpc_closure callback function (functions
 ///    with the signature:
 ///      void (*f)(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error))
-///    then those functions do not automatically own a ref to error
+///    then those functions do not own a ref to error (but are free to manually
+///    take a reference).
 ///  if a grpc_error is passed to *ANY OTHER FUNCTION* then that function takes
 ///    ownership of the error
 /// Errors have:
@@ -171,7 +173,16 @@
                                 gpr_timespec value) GRPC_MUST_USE_RESULT;
 grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which,
                                const char *value) GRPC_MUST_USE_RESULT;
+/// Returns NULL if the specified string is not set.
+/// Caller does NOT own return value.
 const char *grpc_error_get_str(grpc_error *error, grpc_error_strs which);
+
+/// A utility function to get the status code and message to be returned
+/// to the application.  If not set in the top-level message, looks
+/// through child errors until it finds the first one with these attributes.
+void grpc_error_get_status(grpc_error *error, grpc_status_code *code,
+                           const char **msg);
+
 /// Add a child error: an error that is believed to have contributed to this
 /// error occurring. Allows root causing high level errors from lower level
 /// errors that contributed to them.
diff --git a/src/core/lib/iomgr/ev_epoll_linux.c b/src/core/lib/iomgr/ev_epoll_linux.c
index 6a63c4d..ab77ebc 100644
--- a/src/core/lib/iomgr/ev_epoll_linux.c
+++ b/src/core/lib/iomgr/ev_epoll_linux.c
@@ -42,6 +42,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <poll.h>
+#include <pthread.h>
 #include <signal.h>
 #include <string.h>
 #include <sys/epoll.h>
@@ -927,7 +928,8 @@
     fd->polling_island = NULL;
   }
 
-  grpc_exec_ctx_sched(exec_ctx, fd->on_done_closure, error, NULL);
+  grpc_exec_ctx_sched(exec_ctx, fd->on_done_closure, GRPC_ERROR_REF(error),
+                      NULL);
 
   gpr_mu_unlock(&fd->mu);
   UNREF_BY(fd, 2, reason); /* Drop the reference */
@@ -939,6 +941,7 @@
     PI_UNREF(exec_ctx, unref_pi, "fd_orphan");
   }
   GRPC_LOG_IF_ERROR("fd_orphan", GRPC_ERROR_REF(error));
+  GRPC_ERROR_UNREF(error);
 }
 
 static grpc_error *fd_shutdown_error(bool shutdown) {
@@ -1352,8 +1355,10 @@
   gpr_mu_unlock(&pollset->mu);
 
   do {
+    GRPC_SCHEDULING_START_BLOCKING_REGION;
     ep_rv = epoll_pwait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms,
                         sig_mask);
+    GRPC_SCHEDULING_END_BLOCKING_REGION;
     if (ep_rv < 0) {
       if (errno != EINTR) {
         gpr_asprintf(&err_msg,
@@ -1526,6 +1531,8 @@
 
 static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                            grpc_fd *fd) {
+  GPR_TIMER_BEGIN("pollset_add_fd", 0);
+
   grpc_error *error = GRPC_ERROR_NONE;
 
   gpr_mu_lock(&pollset->mu);
@@ -1638,6 +1645,8 @@
   gpr_mu_unlock(&pollset->mu);
 
   GRPC_LOG_IF_ERROR("pollset_add_fd", error);
+
+  GPR_TIMER_END("pollset_add_fd", 0);
 }
 
 /*******************************************************************************
@@ -1888,7 +1897,7 @@
   }
 
   if (!is_grpc_wakeup_signal_initialized) {
-    grpc_use_signal(SIGRTMIN + 2);
+    grpc_use_signal(SIGRTMIN + 6);
   }
 
   fd_global_init();
diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h
index 917f332..4d20ecf 100644
--- a/src/core/lib/iomgr/exec_ctx.h
+++ b/src/core/lib/iomgr/exec_ctx.h
@@ -40,10 +40,9 @@
 
 /** A workqueue represents a list of work to be executed asynchronously.
     Forward declared here to avoid a circular dependency with workqueue.h. */
-struct grpc_workqueue;
 typedef struct grpc_workqueue grpc_workqueue;
+typedef struct grpc_combiner grpc_combiner;
 
-#ifndef GRPC_EXECUTION_CONTEXT_SANITIZER
 /** Execution context.
  *  A bag of data that collects information along a callstack.
  *  Generally created at public API entry points, and passed down as
@@ -58,21 +57,24 @@
  *    should actively try to finish up and get this thread back to its owner
  *
  *  CONVENTIONS:
- *  Instance of this must ALWAYS be constructed on the stack, never
- *  heap allocated. Instances and pointers to them must always be called
- *  exec_ctx. Instances are always passed as the first argument
- *  to a function that takes it, and always as a pointer (grpc_exec_ctx
- *  is never copied).
+ *  - Instance of this must ALWAYS be constructed on the stack, never
+ *    heap allocated.
+ *  - Instances and pointers to them must always be called exec_ctx.
+ *  - Instances are always passed as the first argument to a function that
+ *    takes it, and always as a pointer (grpc_exec_ctx is never copied).
  */
+#ifndef GRPC_EXECUTION_CONTEXT_SANITIZER
 struct grpc_exec_ctx {
   grpc_closure_list closure_list;
+  /** currently active combiner: updated only via combiner.c */
+  grpc_combiner *active_combiner;
   bool cached_ready_to_finish;
   void *check_ready_to_finish_arg;
   bool (*check_ready_to_finish)(grpc_exec_ctx *exec_ctx, void *arg);
 };
 
 #define GRPC_EXEC_CTX_INIT_WITH_FINISH_CHECK(finish_check, finish_check_arg) \
-  { GRPC_CLOSURE_LIST_INIT, false, finish_check_arg, finish_check }
+  { GRPC_CLOSURE_LIST_INIT, NULL, false, finish_check_arg, finish_check }
 #else
 struct grpc_exec_ctx {
   bool cached_ready_to_finish;
diff --git a/src/core/lib/iomgr/socket_windows.c b/src/core/lib/iomgr/socket_windows.c
index d7d5f6f..78ef46d 100644
--- a/src/core/lib/iomgr/socket_windows.c
+++ b/src/core/lib/iomgr/socket_windows.c
@@ -84,7 +84,7 @@
     DisconnectEx(winsocket->socket, NULL, 0, 0);
   } else {
     char *utf8_message = gpr_format_message(WSAGetLastError());
-    gpr_log(GPR_ERROR, "Unable to retrieve DisconnectEx pointer : %s",
+    gpr_log(GPR_INFO, "Unable to retrieve DisconnectEx pointer : %s",
             utf8_message);
     gpr_free(utf8_message);
   }
diff --git a/src/core/lib/iomgr/tcp_client_posix.c b/src/core/lib/iomgr/tcp_client_posix.c
index 80c7a3f..3496b60 100644
--- a/src/core/lib/iomgr/tcp_client_posix.c
+++ b/src/core/lib/iomgr/tcp_client_posix.c
@@ -146,61 +146,57 @@
   grpc_timer_cancel(exec_ctx, &ac->alarm);
 
   gpr_mu_lock(&ac->mu);
-  if (error == GRPC_ERROR_NONE) {
-    do {
-      so_error_size = sizeof(so_error);
-      err = getsockopt(grpc_fd_wrapped_fd(fd), SOL_SOCKET, SO_ERROR, &so_error,
-                       &so_error_size);
-    } while (err < 0 && errno == EINTR);
-    if (err < 0) {
-      error = GRPC_OS_ERROR(errno, "getsockopt");
-      goto finish;
-    } else if (so_error != 0) {
-      if (so_error == ENOBUFS) {
-        /* We will get one of these errors if we have run out of
-           memory in the kernel for the data structures allocated
-           when you connect a socket.  If this happens it is very
-           likely that if we wait a little bit then try again the
-           connection will work (since other programs or this
-           program will close their network connections and free up
-           memory).  This does _not_ indicate that there is anything
-           wrong with the server we are connecting to, this is a
-           local problem.
-
-           If you are looking at this code, then chances are that
-           your program or another program on the same computer
-           opened too many network connections.  The "easy" fix:
-           don't do that! */
-        gpr_log(GPR_ERROR, "kernel out of buffers");
-        gpr_mu_unlock(&ac->mu);
-        grpc_fd_notify_on_write(exec_ctx, fd, &ac->write_closure);
-        return;
-      } else {
-        switch (so_error) {
-          case ECONNREFUSED:
-            error = grpc_error_set_int(error, GRPC_ERROR_INT_ERRNO, errno);
-            error = grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR,
-                                       "Connection refused");
-            break;
-          default:
-            error = GRPC_OS_ERROR(errno, "getsockopt(SO_ERROR)");
-            break;
-        }
-        goto finish;
-      }
-    } else {
-      grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd);
-      *ep = grpc_tcp_create(fd, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, ac->addr_str);
-      fd = NULL;
-      goto finish;
-    }
-  } else {
+  if (error != GRPC_ERROR_NONE) {
     error =
         grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, "Timeout occurred");
     goto finish;
   }
 
-  GPR_UNREACHABLE_CODE(return );
+  do {
+    so_error_size = sizeof(so_error);
+    err = getsockopt(grpc_fd_wrapped_fd(fd), SOL_SOCKET, SO_ERROR, &so_error,
+                     &so_error_size);
+  } while (err < 0 && errno == EINTR);
+  if (err < 0) {
+    error = GRPC_OS_ERROR(errno, "getsockopt");
+    goto finish;
+  }
+
+  switch (so_error) {
+    case 0:
+      grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd);
+      *ep = grpc_tcp_create(fd, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, ac->addr_str);
+      fd = NULL;
+      break;
+    case ENOBUFS:
+      /* We will get one of these errors if we have run out of
+         memory in the kernel for the data structures allocated
+         when you connect a socket.  If this happens it is very
+         likely that if we wait a little bit then try again the
+         connection will work (since other programs or this
+         program will close their network connections and free up
+         memory).  This does _not_ indicate that there is anything
+         wrong with the server we are connecting to, this is a
+         local problem.
+
+         If you are looking at this code, then chances are that
+         your program or another program on the same computer
+         opened too many network connections.  The "easy" fix:
+         don't do that! */
+      gpr_log(GPR_ERROR, "kernel out of buffers");
+      gpr_mu_unlock(&ac->mu);
+      grpc_fd_notify_on_write(exec_ctx, fd, &ac->write_closure);
+      return;
+    case ECONNREFUSED:
+      /* This error shouldn't happen for anything other than connect(). */
+      error = GRPC_OS_ERROR(so_error, "connect");
+      break;
+    default:
+      /* We don't really know which syscall triggered the problem here,
+         so punt by reporting getsockopt(). */
+      error = GRPC_OS_ERROR(so_error, "getsockopt(SO_ERROR)");
+      break;
+  }
 
 finish:
   if (fd != NULL) {
diff --git a/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c
index ec21e03..9276772 100644
--- a/src/core/lib/iomgr/tcp_posix.c
+++ b/src/core/lib/iomgr/tcp_posix.c
@@ -284,7 +284,7 @@
 }
 
 /* returns true if done, false if pending; if returning true, *error is set */
-#define MAX_WRITE_IOVEC 1024
+#define MAX_WRITE_IOVEC 1000
 static bool tcp_flush(grpc_tcp *tcp, grpc_error **error) {
   struct msghdr msg;
   struct iovec iov[MAX_WRITE_IOVEC];
@@ -379,10 +379,19 @@
   }
 
   if (!tcp_flush(tcp, &error)) {
+    if (grpc_tcp_trace) {
+      gpr_log(GPR_DEBUG, "write: delayed");
+    }
     grpc_fd_notify_on_write(exec_ctx, tcp->em_fd, &tcp->write_closure);
   } else {
     cb = tcp->write_cb;
     tcp->write_cb = NULL;
+    if (grpc_tcp_trace) {
+      const char *str = grpc_error_string(error);
+      gpr_log(GPR_DEBUG, "write: %s", str);
+      grpc_error_free_string(str);
+    }
+
     GPR_TIMER_BEGIN("tcp_handle_write.cb", 0);
     cb->cb(exec_ctx, cb->cb_arg, error);
     GPR_TIMER_END("tcp_handle_write.cb", 0);
@@ -425,8 +434,16 @@
   if (!tcp_flush(tcp, &error)) {
     TCP_REF(tcp, "write");
     tcp->write_cb = cb;
+    if (grpc_tcp_trace) {
+      gpr_log(GPR_DEBUG, "write: delayed");
+    }
     grpc_fd_notify_on_write(exec_ctx, tcp->em_fd, &tcp->write_closure);
   } else {
+    if (grpc_tcp_trace) {
+      const char *str = grpc_error_string(error);
+      gpr_log(GPR_DEBUG, "write: %s", str);
+      grpc_error_free_string(str);
+    }
     grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
   }
 
diff --git a/src/core/lib/iomgr/tcp_server_posix.c b/src/core/lib/iomgr/tcp_server_posix.c
index 38ebd2d..2d3f6cf 100644
--- a/src/core/lib/iomgr/tcp_server_posix.c
+++ b/src/core/lib/iomgr/tcp_server_posix.c
@@ -90,10 +90,12 @@
   grpc_closure read_closure;
   grpc_closure destroyed_closure;
   struct grpc_tcp_listener *next;
-  /* When we add a listener, more than one can be created, mainly because of
-     IPv6. A sibling will still be in the normal list, but will be flagged
-     as such. Any action, such as ref or unref, will affect all of the
-     siblings in the list. */
+  /* sibling is a linked list of all listeners for a given port. add_port and
+     clone_port place all new listeners in the same sibling list. A member of
+     the 'sibling' list is also a member of the 'next' list. The head of each
+     sibling list has is_sibling==0, and subsequent members of sibling lists
+     have is_sibling==1. is_sibling allows separate sibling lists to be
+     identified while iterating through 'next'. */
   struct grpc_tcp_listener *sibling;
   int is_sibling;
 };
@@ -138,15 +140,17 @@
 };
 
 static gpr_once check_init = GPR_ONCE_INIT;
-static bool has_so_reuseport;
+static bool has_so_reuseport = false;
 
 static void init(void) {
+#ifndef GPR_MANYLINUX1
   int s = socket(AF_INET, SOCK_STREAM, 0);
   if (s >= 0) {
     has_so_reuseport = GRPC_LOG_IF_ERROR("check for SO_REUSEPORT",
                                          grpc_set_socket_reuse_port(s, 1));
     close(s);
   }
+#endif
 }
 
 grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete,
@@ -306,7 +310,7 @@
 
   GPR_ASSERT(fd >= 0);
 
-  if (so_reuseport) {
+  if (so_reuseport && !grpc_is_unix_socket(addr)) {
     err = grpc_set_socket_reuse_port(fd, 1);
     if (err != GRPC_ERROR_NONE) goto error;
   }
@@ -480,6 +484,9 @@
   return err;
 }
 
+/* Insert count new listeners after listener. Every new listener will have the
+   same listen address as listener (SO_REUSEPORT must be enabled). Every new
+   listener is a sibling of listener. */
 static grpc_error *clone_port(grpc_tcp_listener *listener, unsigned count) {
   grpc_tcp_listener *sp = NULL;
   char *addr_str;
@@ -506,6 +513,11 @@
     sp = gpr_malloc(sizeof(grpc_tcp_listener));
     sp->next = listener->next;
     listener->next = sp;
+    /* sp (the new listener) is a sibling of 'listener' (the original
+       listener). */
+    sp->is_sibling = 1;
+    sp->sibling = listener->sibling;
+    listener->sibling = sp;
     sp->server = listener->server;
     sp->fd = fd;
     sp->emfd = grpc_fd_create(fd, name);
@@ -514,8 +526,6 @@
     sp->port = port;
     sp->port_index = listener->port_index;
     sp->fd_index = listener->fd_index + count - i;
-    sp->is_sibling = 1;
-    sp->sibling = listener->is_sibling ? listener->sibling : listener;
     GPR_ASSERT(sp->emfd);
     while (listener->server->tail->next != NULL) {
       listener->server->tail = listener->server->tail->next;
@@ -685,7 +695,8 @@
   s->pollset_count = pollset_count;
   sp = s->head;
   while (sp != NULL) {
-    if (s->so_reuseport && pollset_count > 1) {
+    if (s->so_reuseport && !grpc_is_unix_socket(&sp->addr.sockaddr) &&
+        pollset_count > 1) {
       GPR_ASSERT(GRPC_LOG_IF_ERROR(
           "clone_port", clone_port(sp, (unsigned)(pollset_count - 1))));
       for (i = 0; i < pollset_count; i++) {
diff --git a/src/core/lib/iomgr/tcp_windows.c b/src/core/lib/iomgr/tcp_windows.c
index 35054c4..448a726 100644
--- a/src/core/lib/iomgr/tcp_windows.c
+++ b/src/core/lib/iomgr/tcp_windows.c
@@ -319,6 +319,7 @@
                             ? GRPC_ERROR_NONE
                             : GRPC_WSA_ERROR(info->wsa_error, "WSASend");
     grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
+    if (allocated) gpr_free(allocated);
     return;
   }
 
diff --git a/src/core/lib/iomgr/udp_server.c b/src/core/lib/iomgr/udp_server.c
index f682881..df68402 100644
--- a/src/core/lib/iomgr/udp_server.c
+++ b/src/core/lib/iomgr/udp_server.c
@@ -170,6 +170,8 @@
       sp->destroyed_closure.cb = destroyed_port;
       sp->destroyed_closure.cb_arg = s;
 
+      /* Call the orphan_cb to signal that the FD is about to be closed and
+       * should no longer be used. */
       GPR_ASSERT(sp->orphan_cb);
       sp->orphan_cb(sp->emfd);
 
@@ -196,6 +198,11 @@
   /* shutdown all fd's */
   if (s->active_ports) {
     for (i = 0; i < s->nports; i++) {
+      /* Call the orphan_cb to signal that the FD is about to be closed and
+       * should no longer be used. */
+      GPR_ASSERT(sp->orphan_cb);
+      sp->orphan_cb(sp->emfd);
+
       grpc_fd_shutdown(exec_ctx, s->ports[i].emfd);
     }
     gpr_mu_unlock(&s->mu);
diff --git a/src/core/lib/iomgr/workqueue.h b/src/core/lib/iomgr/workqueue.h
index 7156e49..b2805dc 100644
--- a/src/core/lib/iomgr/workqueue.h
+++ b/src/core/lib/iomgr/workqueue.h
@@ -50,10 +50,6 @@
 
 /* grpc_workqueue is forward declared in exec_ctx.h */
 
-/* Deprecated: do not use.
-   This has *already* been removed in a future commit. */
-void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue);
-
 /* Reference counting functions. Use the macro's always
    (GRPC_WORKQUEUE_{REF,UNREF}).
 
diff --git a/src/core/lib/iomgr/workqueue_posix.c b/src/core/lib/iomgr/workqueue_posix.c
index e0d6dac..ecfea68 100644
--- a/src/core/lib/iomgr/workqueue_posix.c
+++ b/src/core/lib/iomgr/workqueue_posix.c
@@ -44,6 +44,7 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/profiling/timers.h"
 
 static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error);
 
@@ -52,8 +53,7 @@
   char name[32];
   *workqueue = gpr_malloc(sizeof(grpc_workqueue));
   gpr_ref_init(&(*workqueue)->refs, 1);
-  gpr_mu_init(&(*workqueue)->mu);
-  (*workqueue)->closure_list.head = (*workqueue)->closure_list.tail = NULL;
+  gpr_atm_no_barrier_store(&(*workqueue)->state, 1);
   grpc_error *err = grpc_wakeup_fd_init(&(*workqueue)->wakeup_fd);
   if (err != GRPC_ERROR_NONE) {
     gpr_free(*workqueue);
@@ -62,6 +62,7 @@
   sprintf(name, "workqueue:%p", (void *)(*workqueue));
   (*workqueue)->wakeup_read_fd = grpc_fd_create(
       GRPC_WAKEUP_FD_GET_READ_FD(&(*workqueue)->wakeup_fd), name);
+  gpr_mpscq_init(&(*workqueue)->queue);
   grpc_closure_init(&(*workqueue)->read_closure, on_readable, *workqueue);
   grpc_fd_notify_on_read(exec_ctx, (*workqueue)->wakeup_read_fd,
                          &(*workqueue)->read_closure);
@@ -70,57 +71,79 @@
 
 static void workqueue_destroy(grpc_exec_ctx *exec_ctx,
                               grpc_workqueue *workqueue) {
-  grpc_exec_ctx_enqueue_list(exec_ctx, &workqueue->closure_list, NULL);
   grpc_fd_shutdown(exec_ctx, workqueue->wakeup_read_fd);
 }
 
+static void workqueue_orphan(grpc_exec_ctx *exec_ctx,
+                             grpc_workqueue *workqueue) {
+  if (gpr_atm_full_fetch_add(&workqueue->state, -1) == 1) {
+    workqueue_destroy(exec_ctx, workqueue);
+  }
+}
+
 #ifdef GRPC_WORKQUEUE_REFCOUNT_DEBUG
 void grpc_workqueue_ref(grpc_workqueue *workqueue, const char *file, int line,
                         const char *reason) {
+  if (workqueue == NULL) return;
   gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "WORKQUEUE:%p   ref %d -> %d %s",
           workqueue, (int)workqueue->refs.count, (int)workqueue->refs.count + 1,
           reason);
-#else
-void grpc_workqueue_ref(grpc_workqueue *workqueue) {
-#endif
   gpr_ref(&workqueue->refs);
 }
+#else
+void grpc_workqueue_ref(grpc_workqueue *workqueue) {
+  if (workqueue == NULL) return;
+  gpr_ref(&workqueue->refs);
+}
+#endif
 
 #ifdef GRPC_WORKQUEUE_REFCOUNT_DEBUG
 void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue,
                           const char *file, int line, const char *reason) {
+  if (workqueue == NULL) return;
   gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "WORKQUEUE:%p unref %d -> %d %s",
           workqueue, (int)workqueue->refs.count, (int)workqueue->refs.count - 1,
           reason);
+  if (gpr_unref(&workqueue->refs)) {
+    workqueue_orphan(exec_ctx, workqueue);
+  }
+}
 #else
 void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) {
-#endif
+  if (workqueue == NULL) return;
   if (gpr_unref(&workqueue->refs)) {
-    workqueue_destroy(exec_ctx, workqueue);
+    workqueue_orphan(exec_ctx, workqueue);
+  }
+}
+#endif
+
+static void drain(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) {
+  abort();
+}
+
+static void wakeup(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) {
+  GPR_TIMER_MARK("workqueue.wakeup", 0);
+  grpc_error *err = grpc_wakeup_fd_wakeup(&workqueue->wakeup_fd);
+  if (!GRPC_LOG_IF_ERROR("wakeupfd_wakeup", err)) {
+    drain(exec_ctx, workqueue);
   }
 }
 
-void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) {
-  gpr_mu_lock(&workqueue->mu);
-  grpc_exec_ctx_enqueue_list(exec_ctx, &workqueue->closure_list, NULL);
-  gpr_mu_unlock(&workqueue->mu);
-}
-
 static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  GPR_TIMER_BEGIN("workqueue.on_readable", 0);
+
   grpc_workqueue *workqueue = arg;
 
   if (error != GRPC_ERROR_NONE) {
-    gpr_mu_destroy(&workqueue->mu);
     /* HACK: let wakeup_fd code know that we stole the fd */
     workqueue->wakeup_fd.read_fd = 0;
     grpc_wakeup_fd_destroy(&workqueue->wakeup_fd);
     grpc_fd_orphan(exec_ctx, workqueue->wakeup_read_fd, NULL, NULL, "destroy");
+    GPR_ASSERT(gpr_atm_no_barrier_load(&workqueue->state) == 0);
     gpr_free(workqueue);
   } else {
-    gpr_mu_lock(&workqueue->mu);
-    grpc_exec_ctx_enqueue_list(exec_ctx, &workqueue->closure_list, NULL);
     error = grpc_wakeup_fd_consume_wakeup(&workqueue->wakeup_fd);
-    gpr_mu_unlock(&workqueue->mu);
+    gpr_mpscq_node *n = gpr_mpscq_pop(&workqueue->queue);
     if (error == GRPC_ERROR_NONE) {
       grpc_fd_notify_on_read(exec_ctx, workqueue->wakeup_read_fd,
                              &workqueue->read_closure);
@@ -128,24 +151,46 @@
       /* recurse to get error handling */
       on_readable(exec_ctx, arg, error);
     }
+    if (n == NULL) {
+      /* try again - queue in an inconsistant state */
+      wakeup(exec_ctx, workqueue);
+    } else {
+      switch (gpr_atm_full_fetch_add(&workqueue->state, -2)) {
+        case 3:  // had one count, one unorphaned --> done, unorphaned
+          break;
+        case 2:  // had one count, one orphaned --> done, orphaned
+          workqueue_destroy(exec_ctx, workqueue);
+          break;
+        case 1:
+        case 0:
+          // these values are illegal - representing an already done or
+          // deleted workqueue
+          GPR_UNREACHABLE_CODE(break);
+        default:
+          // schedule a wakeup since there's more to do
+          wakeup(exec_ctx, workqueue);
+      }
+      grpc_closure *cl = (grpc_closure *)n;
+      grpc_error *clerr = cl->error;
+      cl->cb(exec_ctx, cl->cb_arg, clerr);
+      GRPC_ERROR_UNREF(clerr);
+    }
   }
+
+  GPR_TIMER_END("workqueue.on_readable", 0);
 }
 
 void grpc_workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue,
                             grpc_closure *closure, grpc_error *error) {
-  grpc_error *push_error = GRPC_ERROR_NONE;
-  gpr_mu_lock(&workqueue->mu);
-  if (grpc_closure_list_empty(workqueue->closure_list)) {
-    push_error = grpc_wakeup_fd_wakeup(&workqueue->wakeup_fd);
+  GPR_TIMER_BEGIN("workqueue.enqueue", 0);
+  gpr_atm last = gpr_atm_full_fetch_add(&workqueue->state, 2);
+  GPR_ASSERT(last & 1);
+  closure->error = error;
+  gpr_mpscq_push(&workqueue->queue, &closure->next_data.atm_next);
+  if (last == 1) {
+    wakeup(exec_ctx, workqueue);
   }
-  grpc_closure_list_append(&workqueue->closure_list, closure, error);
-  if (push_error != GRPC_ERROR_NONE) {
-    const char *msg = grpc_error_string(push_error);
-    gpr_log(GPR_ERROR, "Failed to push to workqueue: %s", msg);
-    grpc_error_free_string(msg);
-    grpc_exec_ctx_enqueue_list(exec_ctx, &workqueue->closure_list, NULL);
-  }
-  gpr_mu_unlock(&workqueue->mu);
+  GPR_TIMER_END("workqueue.enqueue", 0);
 }
 
 #endif /* GPR_POSIX_SOCKET */
diff --git a/src/core/lib/iomgr/workqueue_posix.h b/src/core/lib/iomgr/workqueue_posix.h
index 0f26ba5..03ee21c 100644
--- a/src/core/lib/iomgr/workqueue_posix.h
+++ b/src/core/lib/iomgr/workqueue_posix.h
@@ -35,14 +35,17 @@
 #define GRPC_CORE_LIB_IOMGR_WORKQUEUE_POSIX_H
 
 #include "src/core/lib/iomgr/wakeup_fd_posix.h"
+#include "src/core/lib/support/mpscq.h"
 
 struct grpc_fd;
 
 struct grpc_workqueue {
   gpr_refcount refs;
-
-  gpr_mu mu;
-  grpc_closure_list closure_list;
+  gpr_mpscq queue;
+  // state is:
+  // lower bit - zero if orphaned
+  // other bits - number of items enqueued
+  gpr_atm state;
 
   grpc_wakeup_fd wakeup_fd;
   struct grpc_fd *wakeup_read_fd;
diff --git a/src/core/lib/iomgr/workqueue_windows.c b/src/core/lib/iomgr/workqueue_windows.c
index 23e2dea..ee81dc2 100644
--- a/src/core/lib/iomgr/workqueue_windows.c
+++ b/src/core/lib/iomgr/workqueue_windows.c
@@ -42,8 +42,6 @@
 // context, which is at least correct, if not performant or in the spirit of
 // workqueues.
 
-void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) {}
-
 #ifdef GRPC_WORKQUEUE_REFCOUNT_DEBUG
 void grpc_workqueue_ref(grpc_workqueue *workqueue, const char *file, int line,
                         const char *reason) {}
diff --git a/src/core/lib/json/json_reader.c b/src/core/lib/json/json_reader.c
index bc04bcc..5b42ca5 100644
--- a/src/core/lib/json/json_reader.c
+++ b/src/core/lib/json/json_reader.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -171,8 +171,9 @@
         switch (reader->state) {
           case GRPC_JSON_STATE_OBJECT_KEY_STRING:
           case GRPC_JSON_STATE_VALUE_STRING:
-            if (reader->unicode_high_surrogate != 0)
+            if (reader->unicode_high_surrogate != 0) {
               return GRPC_JSON_PARSE_ERROR;
+            }
             json_reader_string_add_char(reader, c);
             break;
 
@@ -289,8 +290,9 @@
             break;
 
           case GRPC_JSON_STATE_OBJECT_KEY_STRING:
-            if (reader->unicode_high_surrogate != 0)
+            if (reader->unicode_high_surrogate != 0) {
               return GRPC_JSON_PARSE_ERROR;
+            }
             if (c == '"') {
               reader->state = GRPC_JSON_STATE_OBJECT_KEY_END;
               json_reader_set_key(reader);
@@ -302,8 +304,9 @@
             break;
 
           case GRPC_JSON_STATE_VALUE_STRING:
-            if (reader->unicode_high_surrogate != 0)
+            if (reader->unicode_high_surrogate != 0) {
               return GRPC_JSON_PARSE_ERROR;
+            }
             if (c == '"') {
               reader->state = GRPC_JSON_STATE_VALUE_END;
               json_reader_set_string(reader);
@@ -383,8 +386,9 @@
             } else {
               reader->state = GRPC_JSON_STATE_VALUE_STRING;
             }
-            if (reader->unicode_high_surrogate && c != 'u')
+            if (reader->unicode_high_surrogate && c != 'u') {
               return GRPC_JSON_PARSE_ERROR;
+            }
             switch (c) {
               case '"':
               case '/':
diff --git a/src/core/lib/security/context/security_context.c b/src/core/lib/security/context/security_context.c
index 127b13e..2204fad 100644
--- a/src/core/lib/security/context/security_context.c
+++ b/src/core/lib/security/context/security_context.c
@@ -99,6 +99,9 @@
   grpc_client_security_context *c = (grpc_client_security_context *)ctx;
   grpc_call_credentials_unref(c->creds);
   GRPC_AUTH_CONTEXT_UNREF(c->auth_context, "client_security_context");
+  if (c->extension.instance != NULL && c->extension.destroy != NULL) {
+    c->extension.destroy(c->extension.instance);
+  }
   gpr_free(ctx);
 }
 
@@ -114,6 +117,9 @@
 void grpc_server_security_context_destroy(void *ctx) {
   grpc_server_security_context *c = (grpc_server_security_context *)ctx;
   GRPC_AUTH_CONTEXT_UNREF(c->auth_context, "server_security_context");
+  if (c->extension.instance != NULL && c->extension.destroy != NULL) {
+    c->extension.destroy(c->extension.instance);
+  }
   gpr_free(ctx);
 }
 
diff --git a/src/core/lib/security/context/security_context.h b/src/core/lib/security/context/security_context.h
index ef0c06b..1e131a0 100644
--- a/src/core/lib/security/context/security_context.h
+++ b/src/core/lib/security/context/security_context.h
@@ -37,6 +37,10 @@
 #include "src/core/lib/iomgr/pollset.h"
 #include "src/core/lib/security/credentials/credentials.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* --- grpc_auth_context ---
 
    High level authentication context object. Can optionally be chained. */
@@ -80,6 +84,16 @@
 
 void grpc_auth_property_reset(grpc_auth_property *property);
 
+/* --- grpc_security_context_extension ---
+
+   Extension to the security context that may be set in a filter and accessed
+   later by a higher level method on a grpc_call object. */
+
+typedef struct {
+  void *instance;
+  void (*destroy)(void *);
+} grpc_security_context_extension;
+
 /* --- grpc_client_security_context ---
 
    Internal client-side security context. */
@@ -87,6 +101,7 @@
 typedef struct {
   grpc_call_credentials *creds;
   grpc_auth_context *auth_context;
+  grpc_security_context_extension extension;
 } grpc_client_security_context;
 
 grpc_client_security_context *grpc_client_security_context_create(void);
@@ -98,6 +113,7 @@
 
 typedef struct {
   grpc_auth_context *auth_context;
+  grpc_security_context_extension extension;
 } grpc_server_security_context;
 
 grpc_server_security_context *grpc_server_security_context_create(void);
@@ -111,4 +127,8 @@
 grpc_auth_context *grpc_find_auth_context_in_args(
     const grpc_channel_args *args);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H */
diff --git a/src/core/lib/security/credentials/jwt/json_token.c b/src/core/lib/security/credentials/jwt/json_token.c
index 354c131..192a5f4 100644
--- a/src/core/lib/security/credentials/jwt/json_token.c
+++ b/src/core/lib/security/credentials/jwt/json_token.c
@@ -38,6 +38,7 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
 
 #include "src/core/lib/security/util/b64.h"
 #include "src/core/lib/security/util/json_util.h"
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.c b/src/core/lib/security/credentials/plugin/plugin_credentials.c
index 824ff08..905de37 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.c
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.c
@@ -37,6 +37,7 @@
 
 #include "src/core/lib/surface/api_trace.h"
 
+#include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
@@ -71,17 +72,33 @@
           error_details);
   } else {
     size_t i;
+    bool seen_illegal_header = false;
     grpc_credentials_md *md_array = NULL;
-    if (num_md > 0) {
+    for (i = 0; i < num_md; i++) {
+      if (!grpc_header_key_is_legal(md[i].key, strlen(md[i].key))) {
+        gpr_log(GPR_ERROR, "Plugin added invalid metadata key: %s", md[i].key);
+        seen_illegal_header = true;
+        break;
+      } else if (!grpc_is_binary_header(md[i].key, strlen(md[i].key)) &&
+                 !grpc_header_nonbin_value_is_legal(md[i].value,
+                                                    md[i].value_length)) {
+        gpr_log(GPR_ERROR, "Plugin added invalid metadata value.");
+        seen_illegal_header = true;
+        break;
+      }
+    }
+    if (seen_illegal_header) {
+      r->cb(&exec_ctx, r->user_data, NULL, 0, GRPC_CREDENTIALS_ERROR,
+            "Illegal metadata");
+    } else if (num_md > 0) {
       md_array = gpr_malloc(num_md * sizeof(grpc_credentials_md));
       for (i = 0; i < num_md; i++) {
         md_array[i].key = gpr_slice_from_copied_string(md[i].key);
         md_array[i].value =
             gpr_slice_from_copied_buffer(md[i].value, md[i].value_length);
       }
-    }
-    r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK, NULL);
-    if (md_array != NULL) {
+      r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK,
+            NULL);
       for (i = 0; i < num_md; i++) {
         gpr_slice_unref(md_array[i].key);
         gpr_slice_unref(md_array[i].value);
diff --git a/src/core/lib/security/transport/client_auth_filter.c b/src/core/lib/security/transport/client_auth_filter.c
index ed7929a..b366d14 100644
--- a/src/core/lib/security/transport/client_auth_filter.c
+++ b/src/core/lib/security/transport/client_auth_filter.c
@@ -40,6 +40,7 @@
 #include <grpc/support/string_util.h>
 
 #include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/security/transport/security_connector.h"
@@ -218,6 +219,8 @@
 static void auth_start_transport_op(grpc_exec_ctx *exec_ctx,
                                     grpc_call_element *elem,
                                     grpc_transport_stream_op *op) {
+  GPR_TIMER_BEGIN("auth_start_transport_op", 0);
+
   /* grab pointers to our data from the call element */
   call_data *calld = elem->call_data;
   channel_data *chand = elem->channel_data;
@@ -258,19 +261,23 @@
       grpc_channel_security_connector_check_call_host(
           exec_ctx, chand->security_connector, call_host, chand->auth_context,
           on_host_checked, elem);
+      GPR_TIMER_END("auth_start_transport_op", 0);
       return; /* early exit */
     }
   }
 
   /* pass control down the stack */
   grpc_call_next_op(exec_ctx, elem, op);
+  GPR_TIMER_END("auth_start_transport_op", 0);
 }
 
 /* Constructor for call_data */
-static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {
+static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  grpc_call_element_args *args) {
   call_data *calld = elem->call_data;
   memset(calld, 0, sizeof(*calld));
+  return GRPC_ERROR_NONE;
 }
 
 static void set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
@@ -282,7 +289,8 @@
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {
   call_data *calld = elem->call_data;
   grpc_call_credentials_unref(calld->creds);
   if (calld->host != NULL) {
diff --git a/src/core/lib/security/transport/handshake.c b/src/core/lib/security/transport/handshake.c
index 540a172..fbeec31 100644
--- a/src/core/lib/security/transport/handshake.c
+++ b/src/core/lib/security/transport/handshake.c
@@ -325,8 +325,9 @@
 void grpc_do_security_handshake(
     grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker,
     grpc_security_connector *connector, bool is_client_side,
-    grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
-    grpc_security_handshake_done_cb cb, void *user_data) {
+    grpc_endpoint *nonsecure_endpoint, gpr_slice_buffer *read_buffer,
+    gpr_timespec deadline, grpc_security_handshake_done_cb cb,
+    void *user_data) {
   grpc_security_connector_handshake_list *handshake_node;
   grpc_security_handshake *h = gpr_malloc(sizeof(grpc_security_handshake));
   memset(h, 0, sizeof(grpc_security_handshake));
@@ -346,6 +347,10 @@
   gpr_slice_buffer_init(&h->left_overs);
   gpr_slice_buffer_init(&h->outgoing);
   gpr_slice_buffer_init(&h->incoming);
+  if (read_buffer != NULL) {
+    gpr_slice_buffer_move_into(read_buffer, &h->incoming);
+    gpr_free(read_buffer);
+  }
   if (!is_client_side) {
     grpc_server_security_connector *server_connector =
         (grpc_server_security_connector *)connector;
diff --git a/src/core/lib/security/transport/handshake.h b/src/core/lib/security/transport/handshake.h
index c0906dd..53092f5 100644
--- a/src/core/lib/security/transport/handshake.h
+++ b/src/core/lib/security/transport/handshake.h
@@ -37,12 +37,13 @@
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/security/transport/security_connector.h"
 
-/* Calls the callback upon completion. Takes owership of handshaker. */
+/* Calls the callback upon completion. Takes owership of handshaker and
+ * read_buffer. */
 void grpc_do_security_handshake(
     grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker,
     grpc_security_connector *connector, bool is_client_side,
-    grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
-    grpc_security_handshake_done_cb cb, void *user_data);
+    grpc_endpoint *nonsecure_endpoint, gpr_slice_buffer *read_buffer,
+    gpr_timespec deadline, grpc_security_handshake_done_cb cb, void *user_data);
 
 void grpc_security_handshake_shutdown(grpc_exec_ctx *exec_ctx, void *handshake);
 
diff --git a/src/core/lib/security/transport/secure_endpoint.c b/src/core/lib/security/transport/secure_endpoint.c
index bc50f9d..acb0113 100644
--- a/src/core/lib/security/transport/secure_endpoint.c
+++ b/src/core/lib/security/transport/secure_endpoint.c
@@ -38,6 +38,7 @@
 #include <grpc/support/slice_buffer.h>
 #include <grpc/support/sync.h>
 #include "src/core/lib/debug/trace.h"
+#include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/security/transport/tsi_error.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/tsi/transport_security_interface.h"
@@ -128,7 +129,7 @@
 
 static void call_read_cb(grpc_exec_ctx *exec_ctx, secure_endpoint *ep,
                          grpc_error *error) {
-  if (false && grpc_trace_secure_endpoint) {
+  if (grpc_trace_secure_endpoint) {
     size_t i;
     for (i = 0; i < ep->read_buffer->count; i++) {
       char *data = gpr_dump_slice(ep->read_buffer->slices[i],
@@ -248,6 +249,8 @@
 
 static void endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep,
                            gpr_slice_buffer *slices, grpc_closure *cb) {
+  GPR_TIMER_BEGIN("secure_endpoint.endpoint_write", 0);
+
   unsigned i;
   tsi_result result = TSI_OK;
   secure_endpoint *ep = (secure_endpoint *)secure_ep;
@@ -256,7 +259,7 @@
 
   gpr_slice_buffer_reset_and_unref(&ep->output_buffer);
 
-  if (false && grpc_trace_secure_endpoint) {
+  if (grpc_trace_secure_endpoint) {
     for (i = 0; i < slices->count; i++) {
       char *data =
           gpr_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -323,10 +326,12 @@
         exec_ctx, cb,
         grpc_set_tsi_error_result(GRPC_ERROR_CREATE("Wrap failed"), result),
         NULL);
+    GPR_TIMER_END("secure_endpoint.endpoint_write", 0);
     return;
   }
 
   grpc_endpoint_write(exec_ctx, ep->wrapped_ep, &ep->output_buffer, cb);
+  GPR_TIMER_END("secure_endpoint.endpoint_write", 0);
 }
 
 static void endpoint_shutdown(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/lib/security/transport/security_connector.c b/src/core/lib/security/transport/security_connector.c
index f0ee677..0eca46e 100644
--- a/src/core/lib/security/transport/security_connector.c
+++ b/src/core/lib/security/transport/security_connector.c
@@ -127,25 +127,29 @@
 
 void grpc_channel_security_connector_do_handshake(
     grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc,
-    grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
-    grpc_security_handshake_done_cb cb, void *user_data) {
+    grpc_endpoint *nonsecure_endpoint, gpr_slice_buffer *read_buffer,
+    gpr_timespec deadline, grpc_security_handshake_done_cb cb,
+    void *user_data) {
   if (sc == NULL || nonsecure_endpoint == NULL) {
+    gpr_free(read_buffer);
     cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
   } else {
-    sc->do_handshake(exec_ctx, sc, nonsecure_endpoint, deadline, cb, user_data);
+    sc->do_handshake(exec_ctx, sc, nonsecure_endpoint, read_buffer, deadline,
+                     cb, user_data);
   }
 }
 
 void grpc_server_security_connector_do_handshake(
     grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
     grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
-    gpr_timespec deadline, grpc_security_handshake_done_cb cb,
-    void *user_data) {
+    gpr_slice_buffer *read_buffer, gpr_timespec deadline,
+    grpc_security_handshake_done_cb cb, void *user_data) {
   if (sc == NULL || nonsecure_endpoint == NULL) {
+    gpr_free(read_buffer);
     cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
   } else {
-    sc->do_handshake(exec_ctx, sc, acceptor, nonsecure_endpoint, deadline, cb,
-                     user_data);
+    sc->do_handshake(exec_ctx, sc, acceptor, nonsecure_endpoint, read_buffer,
+                     deadline, cb, user_data);
   }
 }
 
@@ -312,23 +316,23 @@
 static void fake_channel_do_handshake(grpc_exec_ctx *exec_ctx,
                                       grpc_channel_security_connector *sc,
                                       grpc_endpoint *nonsecure_endpoint,
+                                      gpr_slice_buffer *read_buffer,
                                       gpr_timespec deadline,
                                       grpc_security_handshake_done_cb cb,
                                       void *user_data) {
   grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(1), &sc->base,
-                             true, nonsecure_endpoint, deadline, cb, user_data);
+                             true, nonsecure_endpoint, read_buffer, deadline,
+                             cb, user_data);
 }
 
-static void fake_server_do_handshake(grpc_exec_ctx *exec_ctx,
-                                     grpc_server_security_connector *sc,
-                                     grpc_tcp_server_acceptor *acceptor,
-                                     grpc_endpoint *nonsecure_endpoint,
-                                     gpr_timespec deadline,
-                                     grpc_security_handshake_done_cb cb,
-                                     void *user_data) {
+static void fake_server_do_handshake(
+    grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
+    grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
+    gpr_slice_buffer *read_buffer, gpr_timespec deadline,
+    grpc_security_handshake_done_cb cb, void *user_data) {
   grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(0), &sc->base,
-                             false, nonsecure_endpoint, deadline, cb,
-                             user_data);
+                             false, nonsecure_endpoint, read_buffer, deadline,
+                             cb, user_data);
 }
 
 static grpc_security_connector_vtable fake_channel_vtable = {
@@ -418,6 +422,7 @@
 static void ssl_channel_do_handshake(grpc_exec_ctx *exec_ctx,
                                      grpc_channel_security_connector *sc,
                                      grpc_endpoint *nonsecure_endpoint,
+                                     gpr_slice_buffer *read_buffer,
                                      gpr_timespec deadline,
                                      grpc_security_handshake_done_cb cb,
                                      void *user_data) {
@@ -430,30 +435,32 @@
                                         : c->target_name,
       &handshaker);
   if (status != GRPC_SECURITY_OK) {
+    gpr_free(read_buffer);
     cb(exec_ctx, user_data, status, NULL, NULL);
   } else {
     grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, true,
-                               nonsecure_endpoint, deadline, cb, user_data);
+                               nonsecure_endpoint, read_buffer, deadline, cb,
+                               user_data);
   }
 }
 
-static void ssl_server_do_handshake(grpc_exec_ctx *exec_ctx,
-                                    grpc_server_security_connector *sc,
-                                    grpc_tcp_server_acceptor *acceptor,
-                                    grpc_endpoint *nonsecure_endpoint,
-                                    gpr_timespec deadline,
-                                    grpc_security_handshake_done_cb cb,
-                                    void *user_data) {
+static void ssl_server_do_handshake(
+    grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
+    grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
+    gpr_slice_buffer *read_buffer, gpr_timespec deadline,
+    grpc_security_handshake_done_cb cb, void *user_data) {
   grpc_ssl_server_security_connector *c =
       (grpc_ssl_server_security_connector *)sc;
   tsi_handshaker *handshaker;
   grpc_security_status status =
       ssl_create_handshaker(c->handshaker_factory, false, NULL, &handshaker);
   if (status != GRPC_SECURITY_OK) {
+    gpr_free(read_buffer);
     cb(exec_ctx, user_data, status, NULL, NULL);
   } else {
     grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, false,
-                               nonsecure_endpoint, deadline, cb, user_data);
+                               nonsecure_endpoint, read_buffer, deadline, cb,
+                               user_data);
   }
 }
 
diff --git a/src/core/lib/security/transport/security_connector.h b/src/core/lib/security/transport/security_connector.h
index c2ddf5e..0b5b44b 100644
--- a/src/core/lib/security/transport/security_connector.h
+++ b/src/core/lib/security/transport/security_connector.h
@@ -143,7 +143,8 @@
                           grpc_security_call_host_check_cb cb, void *user_data);
   void (*do_handshake)(grpc_exec_ctx *exec_ctx,
                        grpc_channel_security_connector *sc,
-                       grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
+                       grpc_endpoint *nonsecure_endpoint,
+                       gpr_slice_buffer *read_buffer, gpr_timespec deadline,
                        grpc_security_handshake_done_cb cb, void *user_data);
 };
 
@@ -156,8 +157,8 @@
 /* Handshake. */
 void grpc_channel_security_connector_do_handshake(
     grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *connector,
-    grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
-    grpc_security_handshake_done_cb cb, void *user_data);
+    grpc_endpoint *nonsecure_endpoint, gpr_slice_buffer *read_buffer,
+    gpr_timespec deadline, grpc_security_handshake_done_cb cb, void *user_data);
 
 /* --- server_security_connector object. ---
 
@@ -174,14 +175,16 @@
   void (*do_handshake)(grpc_exec_ctx *exec_ctx,
                        grpc_server_security_connector *sc,
                        grpc_tcp_server_acceptor *acceptor,
-                       grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
+                       grpc_endpoint *nonsecure_endpoint,
+                       gpr_slice_buffer *read_buffer, gpr_timespec deadline,
                        grpc_security_handshake_done_cb cb, void *user_data);
 };
 
 void grpc_server_security_connector_do_handshake(
     grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
     grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
-    gpr_timespec deadline, grpc_security_handshake_done_cb cb, void *user_data);
+    gpr_slice_buffer *read_buffer, gpr_timespec deadline,
+    grpc_security_handshake_done_cb cb, void *user_data);
 
 void grpc_server_security_connector_shutdown(
     grpc_exec_ctx *exec_ctx, grpc_server_security_connector *connector);
diff --git a/src/core/lib/security/transport/server_auth_filter.c b/src/core/lib/security/transport/server_auth_filter.c
index 12e789b..b2c6815 100644
--- a/src/core/lib/security/transport/server_auth_filter.c
+++ b/src/core/lib/security/transport/server_auth_filter.c
@@ -48,7 +48,7 @@
      up-call on transport_op, and remember to call our on_done_recv member after
      handling it. */
   grpc_closure auth_on_recv;
-  grpc_transport_stream_op transport_op;
+  grpc_transport_stream_op *transport_op;
   grpc_metadata_array md;
   const grpc_metadata *consumed_md;
   size_t num_consumed_md;
@@ -106,6 +106,10 @@
   return md;
 }
 
+static void destroy_op(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  gpr_free(arg);
+}
+
 /* called from application code */
 static void on_md_processing_done(
     void *user_data, const grpc_metadata *consumed_md, size_t num_consumed_md,
@@ -131,21 +135,22 @@
     grpc_exec_ctx_sched(&exec_ctx, calld->on_done_recv, GRPC_ERROR_NONE, NULL);
   } else {
     gpr_slice message;
-    grpc_transport_stream_op close_op;
-    memset(&close_op, 0, sizeof(close_op));
+    grpc_transport_stream_op *close_op = gpr_malloc(sizeof(*close_op));
+    memset(close_op, 0, sizeof(*close_op));
     grpc_metadata_array_destroy(&calld->md);
     error_details = error_details != NULL
                         ? error_details
                         : "Authentication metadata processing failed.";
     message = gpr_slice_from_copied_string(error_details);
-    calld->transport_op.send_initial_metadata = NULL;
-    if (calld->transport_op.send_message != NULL) {
-      grpc_byte_stream_destroy(&exec_ctx, calld->transport_op.send_message);
-      calld->transport_op.send_message = NULL;
+    calld->transport_op->send_initial_metadata = NULL;
+    if (calld->transport_op->send_message != NULL) {
+      grpc_byte_stream_destroy(&exec_ctx, calld->transport_op->send_message);
+      calld->transport_op->send_message = NULL;
     }
-    calld->transport_op.send_trailing_metadata = NULL;
-    grpc_transport_stream_op_add_close(&close_op, status, &message);
-    grpc_call_next_op(&exec_ctx, elem, &close_op);
+    calld->transport_op->send_trailing_metadata = NULL;
+    close_op->on_complete = grpc_closure_create(destroy_op, close_op);
+    grpc_transport_stream_op_add_close(close_op, status, &message);
+    grpc_call_next_op(&exec_ctx, elem, close_op);
     grpc_exec_ctx_sched(&exec_ctx, calld->on_done_recv,
                         grpc_error_set_int(GRPC_ERROR_CREATE(error_details),
                                            GRPC_ERROR_INT_GRPC_STATUS, status),
@@ -182,7 +187,7 @@
     calld->recv_initial_metadata = op->recv_initial_metadata;
     calld->on_done_recv = op->recv_initial_metadata_ready;
     op->recv_initial_metadata_ready = &calld->auth_on_recv;
-    calld->transport_op = *op;
+    calld->transport_op = op;
   }
 }
 
@@ -199,8 +204,9 @@
 }
 
 /* Constructor for call_data */
-static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {
+static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  grpc_call_element_args *args) {
   /* grab pointers to our data from the call element */
   call_data *calld = elem->call_data;
   channel_data *chand = elem->channel_data;
@@ -222,11 +228,14 @@
   args->context[GRPC_CONTEXT_SECURITY].value = server_ctx;
   args->context[GRPC_CONTEXT_SECURITY].destroy =
       grpc_server_security_context_destroy;
+
+  return GRPC_ERROR_NONE;
 }
 
 /* Destructor for call_data */
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {}
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {}
 
 /* Constructor for channel_data */
 static void init_channel_elem(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/lib/support/log_linux.c b/src/core/lib/support/log_linux.c
index 508fae4..299b377 100644
--- a/src/core/lib/support/log_linux.c
+++ b/src/core/lib/support/log_linux.c
@@ -47,7 +47,6 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/time.h>
-#include <linux/unistd.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
diff --git a/src/core/lib/support/log_posix.c b/src/core/lib/support/log_posix.c
index 6ae6320..f972da0 100644
--- a/src/core/lib/support/log_posix.c
+++ b/src/core/lib/support/log_posix.c
@@ -33,7 +33,7 @@
 
 #include <grpc/support/port_platform.h>
 
-#if defined(GPR_POSIX_LOG)
+#ifdef GPR_POSIX_LOG
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
diff --git a/src/core/lib/support/mpscq.c b/src/core/lib/support/mpscq.c
new file mode 100644
index 0000000..5b93232
--- /dev/null
+++ b/src/core/lib/support/mpscq.c
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2016, 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/lib/support/mpscq.h"
+
+#include <grpc/support/log.h>
+
+void gpr_mpscq_init(gpr_mpscq *q) {
+  gpr_atm_no_barrier_store(&q->head, (gpr_atm)&q->stub);
+  q->tail = &q->stub;
+  gpr_atm_no_barrier_store(&q->stub.next, (gpr_atm)NULL);
+}
+
+void gpr_mpscq_destroy(gpr_mpscq *q) {
+  GPR_ASSERT(gpr_atm_no_barrier_load(&q->head) == (gpr_atm)&q->stub);
+  GPR_ASSERT(q->tail == &q->stub);
+}
+
+void gpr_mpscq_push(gpr_mpscq *q, gpr_mpscq_node *n) {
+  gpr_atm_no_barrier_store(&n->next, (gpr_atm)NULL);
+  gpr_mpscq_node *prev =
+      (gpr_mpscq_node *)gpr_atm_full_xchg(&q->head, (gpr_atm)n);
+  gpr_atm_rel_store(&prev->next, (gpr_atm)n);
+}
+
+gpr_mpscq_node *gpr_mpscq_pop(gpr_mpscq *q) {
+  gpr_mpscq_node *tail = q->tail;
+  gpr_mpscq_node *next = (gpr_mpscq_node *)gpr_atm_acq_load(&tail->next);
+  if (tail == &q->stub) {
+    // indicates the list is actually (ephemerally) empty
+    if (next == NULL) return NULL;
+    q->tail = next;
+    tail = next;
+    next = (gpr_mpscq_node *)gpr_atm_acq_load(&tail->next);
+  }
+  if (next != NULL) {
+    q->tail = next;
+    return tail;
+  }
+  gpr_mpscq_node *head = (gpr_mpscq_node *)gpr_atm_acq_load(&q->head);
+  if (tail != head) {
+    // indicates a retry is in order: we're still adding
+    return NULL;
+  }
+  gpr_mpscq_push(q, &q->stub);
+  next = (gpr_mpscq_node *)gpr_atm_acq_load(&tail->next);
+  if (next != NULL) {
+    q->tail = next;
+    return tail;
+  }
+  // indicates a retry is in order: we're still adding
+  return NULL;
+}
diff --git a/src/core/lib/support/mpscq.h b/src/core/lib/support/mpscq.h
new file mode 100644
index 0000000..977a117
--- /dev/null
+++ b/src/core/lib/support/mpscq.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright 2016, 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_CORE_LIB_SUPPORT_MPSCQ_H
+#define GRPC_CORE_LIB_SUPPORT_MPSCQ_H
+
+#include <grpc/support/atm.h>
+#include <stddef.h>
+
+// Multiple-producer single-consumer lock free queue, based upon the
+// implementation from Dmitry Vyukov here:
+// http://www.1024cores.net/home/lock-free-algorithms/queues/intrusive-mpsc-node-based-queue
+
+// List node (include this in a data structure at the top, and add application
+// fields after it - to simulate inheritance)
+typedef struct gpr_mpscq_node { gpr_atm next; } gpr_mpscq_node;
+
+// Actual queue type
+typedef struct gpr_mpscq {
+  gpr_atm head;
+  // make sure head & tail don't share a cacheline
+  char padding[GPR_CACHELINE_SIZE];
+  gpr_mpscq_node *tail;
+  gpr_mpscq_node stub;
+} gpr_mpscq;
+
+void gpr_mpscq_init(gpr_mpscq *q);
+void gpr_mpscq_destroy(gpr_mpscq *q);
+// Push a node
+void gpr_mpscq_push(gpr_mpscq *q, gpr_mpscq_node *n);
+// Pop a node (returns NULL if no node is ready - which doesn't indicate that
+// the queue is empty!!)
+gpr_mpscq_node *gpr_mpscq_pop(gpr_mpscq *q);
+
+#endif /* GRPC_CORE_LIB_SUPPORT_MPSCQ_H */
diff --git a/src/core/lib/support/percent_encoding.c b/src/core/lib/support/percent_encoding.c
new file mode 100644
index 0000000..3c19f26
--- /dev/null
+++ b/src/core/lib/support/percent_encoding.c
@@ -0,0 +1,180 @@
+/*
+ *
+ * Copyright 2016, 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/lib/support/percent_encoding.h"
+
+#include <grpc/support/log.h>
+
+const uint8_t gpr_url_percent_encoding_unreserved_bytes[256 / 8] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0x03, 0xfe, 0xff, 0xff,
+    0x87, 0xfe, 0xff, 0xff, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+const uint8_t gpr_compatible_percent_encoding_unreserved_bytes[256 / 8] = {
+    0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static bool is_unreserved_character(uint8_t c,
+                                    const uint8_t *unreserved_bytes) {
+  return ((unreserved_bytes[c / 8] >> (c % 8)) & 1) != 0;
+}
+
+gpr_slice gpr_percent_encode_slice(gpr_slice slice,
+                                   const uint8_t *unreserved_bytes) {
+  static const uint8_t hex[] = "0123456789ABCDEF";
+
+  // first pass: count the number of bytes needed to output this string
+  size_t output_length = 0;
+  const uint8_t *slice_start = GPR_SLICE_START_PTR(slice);
+  const uint8_t *slice_end = GPR_SLICE_END_PTR(slice);
+  const uint8_t *p;
+  bool any_reserved_bytes = false;
+  for (p = slice_start; p < slice_end; p++) {
+    bool unres = is_unreserved_character(*p, unreserved_bytes);
+    output_length += unres ? 1 : 3;
+    any_reserved_bytes |= !unres;
+  }
+  // no unreserved bytes: return the string unmodified
+  if (!any_reserved_bytes) {
+    return gpr_slice_ref(slice);
+  }
+  // second pass: actually encode
+  gpr_slice out = gpr_slice_malloc(output_length);
+  uint8_t *q = GPR_SLICE_START_PTR(out);
+  for (p = slice_start; p < slice_end; p++) {
+    if (is_unreserved_character(*p, unreserved_bytes)) {
+      *q++ = *p;
+    } else {
+      *q++ = '%';
+      *q++ = hex[*p >> 4];
+      *q++ = hex[*p & 15];
+    }
+  }
+  GPR_ASSERT(q == GPR_SLICE_END_PTR(out));
+  return out;
+}
+
+static bool valid_hex(const uint8_t *p, const uint8_t *end) {
+  if (p >= end) return false;
+  return (*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') ||
+         (*p >= 'A' && *p <= 'F');
+}
+
+static uint8_t dehex(uint8_t c) {
+  if (c >= '0' && c <= '9') return (uint8_t)(c - '0');
+  if (c >= 'A' && c <= 'F') return (uint8_t)(c - 'A' + 10);
+  if (c >= 'a' && c <= 'f') return (uint8_t)(c - 'a' + 10);
+  GPR_UNREACHABLE_CODE(return 255);
+}
+
+bool gpr_strict_percent_decode_slice(gpr_slice slice_in,
+                                     const uint8_t *unreserved_bytes,
+                                     gpr_slice *slice_out) {
+  const uint8_t *p = GPR_SLICE_START_PTR(slice_in);
+  const uint8_t *in_end = GPR_SLICE_END_PTR(slice_in);
+  size_t out_length = 0;
+  bool any_percent_encoded_stuff = false;
+  while (p != in_end) {
+    if (*p == '%') {
+      if (!valid_hex(++p, in_end)) return false;
+      if (!valid_hex(++p, in_end)) return false;
+      p++;
+      out_length++;
+      any_percent_encoded_stuff = true;
+    } else if (is_unreserved_character(*p, unreserved_bytes)) {
+      p++;
+      out_length++;
+    } else {
+      return false;
+    }
+  }
+  if (!any_percent_encoded_stuff) {
+    *slice_out = gpr_slice_ref(slice_in);
+    return true;
+  }
+  p = GPR_SLICE_START_PTR(slice_in);
+  *slice_out = gpr_slice_malloc(out_length);
+  uint8_t *q = GPR_SLICE_START_PTR(*slice_out);
+  while (p != in_end) {
+    if (*p == '%') {
+      *q++ = (uint8_t)(dehex(p[1]) << 4) | (dehex(p[2]));
+      p += 3;
+    } else {
+      *q++ = *p++;
+    }
+  }
+  GPR_ASSERT(q == GPR_SLICE_END_PTR(*slice_out));
+  return true;
+}
+
+gpr_slice gpr_permissive_percent_decode_slice(gpr_slice slice_in) {
+  const uint8_t *p = GPR_SLICE_START_PTR(slice_in);
+  const uint8_t *in_end = GPR_SLICE_END_PTR(slice_in);
+  size_t out_length = 0;
+  bool any_percent_encoded_stuff = false;
+  while (p != in_end) {
+    if (*p == '%') {
+      if (!valid_hex(p + 1, in_end) || !valid_hex(p + 2, in_end)) {
+        p++;
+        out_length++;
+      } else {
+        p += 3;
+        out_length++;
+        any_percent_encoded_stuff = true;
+      }
+    } else {
+      p++;
+      out_length++;
+    }
+  }
+  if (!any_percent_encoded_stuff) {
+    return gpr_slice_ref(slice_in);
+  }
+  p = GPR_SLICE_START_PTR(slice_in);
+  gpr_slice out = gpr_slice_malloc(out_length);
+  uint8_t *q = GPR_SLICE_START_PTR(out);
+  while (p != in_end) {
+    if (*p == '%') {
+      if (!valid_hex(p + 1, in_end) || !valid_hex(p + 2, in_end)) {
+        *q++ = *p++;
+      } else {
+        *q++ = (uint8_t)(dehex(p[1]) << 4) | (dehex(p[2]));
+        p += 3;
+      }
+    } else {
+      *q++ = *p++;
+    }
+  }
+  GPR_ASSERT(q == GPR_SLICE_END_PTR(out));
+  return out;
+}
diff --git a/src/core/lib/support/percent_encoding.h b/src/core/lib/support/percent_encoding.h
new file mode 100644
index 0000000..000bf14
--- /dev/null
+++ b/src/core/lib/support/percent_encoding.h
@@ -0,0 +1,78 @@
+/*
+ *
+ * Copyright 2016, 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_CORE_LIB_SUPPORT_PERCENT_ENCODING_H
+#define GRPC_CORE_LIB_SUPPORT_PERCENT_ENCODING_H
+
+/* Percent encoding and decoding of slices.
+   Transforms arbitrary strings into safe-for-transmission strings by using
+   variants of percent encoding (RFC 3986).
+   Two major variants are supplied: one that strictly matches URL encoding,
+     and another which applies percent encoding only to non-http2 header
+     bytes (the 'compatible' variant) */
+
+#include <stdbool.h>
+
+#include <grpc/support/slice.h>
+
+/* URL percent encoding spec bitfield (usabel as 'unreserved_bytes' in
+   gpr_percent_encode_slice, gpr_strict_percent_decode_slice).
+   Flags [A-Za-z0-9-_.~] as unreserved bytes for the percent encoding routines
+   */
+extern const uint8_t gpr_url_percent_encoding_unreserved_bytes[256 / 8];
+/* URL percent encoding spec bitfield (usabel as 'unreserved_bytes' in
+   gpr_percent_encode_slice, gpr_strict_percent_decode_slice).
+   Flags ascii7 non-control characters excluding '%' as unreserved bytes for the
+   percent encoding routines */
+extern const uint8_t gpr_compatible_percent_encoding_unreserved_bytes[256 / 8];
+
+/* Percent-encode a slice, returning the new slice (this cannot fail):
+   unreserved_bytes is a bitfield indicating which bytes are considered
+   unreserved and thus do not need percent encoding */
+gpr_slice gpr_percent_encode_slice(gpr_slice slice,
+                                   const uint8_t *unreserved_bytes);
+/* Percent-decode a slice, strictly.
+   If the input is legal (contains no unreserved bytes, and legal % encodings),
+   returns true and sets *slice_out to the decoded slice.
+   If the input is not legal, returns false and leaves *slice_out untouched.
+   unreserved_bytes is a bitfield indicating which bytes are considered
+   unreserved and thus do not need percent encoding */
+bool gpr_strict_percent_decode_slice(gpr_slice slice_in,
+                                     const uint8_t *unreserved_bytes,
+                                     gpr_slice *slice_out);
+/* Percent-decode a slice, permissively.
+   If a % triplet can not be decoded, pass it through verbatim.
+   This cannot fail. */
+gpr_slice gpr_permissive_percent_decode_slice(gpr_slice slice_in);
+
+#endif /* GRPC_CORE_LIB_SUPPORT_PERCENT_ENCODING_H */
diff --git a/src/core/lib/support/time_posix.c b/src/core/lib/support/time_posix.c
index 1154207..a69c501 100644
--- a/src/core/lib/support/time_posix.c
+++ b/src/core/lib/support/time_posix.c
@@ -127,7 +127,7 @@
       now.tv_nsec = now_tv.tv_usec * 1000;
       break;
     case GPR_CLOCK_MONOTONIC:
-      now_dbl = (mach_absolute_time() - g_time_start) * g_time_scale;
+      now_dbl = ((double)(mach_absolute_time() - g_time_start)) * g_time_scale;
       now.tv_sec = (int64_t)(now_dbl * 1e-9);
       now.tv_nsec = (int32_t)(now_dbl - ((double)now.tv_sec) * 1e9);
       break;
diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
index fc9df76..5690bca 100644
--- a/src/core/lib/surface/call.c
+++ b/src/core/lib/surface/call.c
@@ -109,6 +109,10 @@
   uint8_t recv_message;
   uint8_t recv_final_op;
   uint8_t is_notify_tag_closure;
+
+  /* TODO(ctiller): now that this is inlined, figure out how much of the above
+                    state can be eliminated */
+  grpc_transport_stream_op op;
 } batch_control;
 
 struct grpc_call {
@@ -122,8 +126,6 @@
 
   /* client or server call */
   bool is_client;
-  /* is the alarm set */
-  bool have_alarm;
   /** has grpc_call_destroy been called */
   bool destroy_called;
   /** flag indicating that cancellation is inherited */
@@ -154,8 +156,9 @@
   /* Received call statuses from various sources */
   received_status status[STATUS_SOURCE_COUNT];
 
-  /* Call stats: only valid after trailing metadata received */
-  grpc_call_stats stats;
+  /* Call data useful used for reporting. Only valid after the call has
+   * completed */
+  grpc_call_final_info final_info;
 
   /* Compression algorithm for *incoming* data */
   grpc_compression_algorithm incoming_compression_algorithm;
@@ -165,9 +168,6 @@
   /* Contexts for various subsystems (security, tracing, ...). */
   grpc_call_context_element context[GRPC_CONTEXT_COUNT];
 
-  /* Deadline alarm - if have_alarm is non-zero */
-  grpc_timer alarm;
-
   /* for the client, extra metadata is initial metadata; for the
      server, it's trailing metadata */
   grpc_linked_mdelem send_extra_metadata[MAX_SEND_EXTRA_METADATA_COUNT];
@@ -210,8 +210,6 @@
 #define CALL_FROM_TOP_ELEM(top_elem) \
   CALL_FROM_CALL_STACK(grpc_call_stack_from_top_element(top_elem))
 
-static void set_deadline_alarm(grpc_exec_ctx *exec_ctx, grpc_call *call,
-                               gpr_timespec deadline);
 static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call,
                        grpc_transport_stream_op *op);
 static grpc_call_error cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
@@ -259,29 +257,8 @@
       call->metadata_batch[i][j].deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
     }
   }
-  call->send_deadline =
-      gpr_convert_clock_type(send_deadline, GPR_CLOCK_MONOTONIC);
-  GRPC_CHANNEL_INTERNAL_REF(channel, "call");
-  /* initial refcount dropped by grpc_call_destroy */
-  grpc_call_stack_init(&exec_ctx, channel_stack, 1, destroy_call, call,
-                       call->context, server_transport_data,
-                       CALL_STACK_FROM_CALL(call));
-  if (cq != NULL) {
-    GPR_ASSERT(
-        pollset_set_alternative == NULL &&
-        "Only one of 'cq' and 'pollset_set_alternative' should be non-NULL.");
-    GRPC_CQ_INTERNAL_REF(cq, "bind");
-    call->pollent =
-        grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq));
-  }
-  if (pollset_set_alternative != NULL) {
-    call->pollent =
-        grpc_polling_entity_create_from_pollset_set(pollset_set_alternative);
-  }
-  if (!grpc_polling_entity_is_empty(&call->pollent)) {
-    grpc_call_stack_set_pollset_or_pollset_set(
-        &exec_ctx, CALL_STACK_FROM_CALL(call), &call->pollent);
-  }
+  send_deadline = gpr_convert_clock_type(send_deadline, GPR_CLOCK_MONOTONIC);
+
   if (parent_call != NULL) {
     GRPC_CALL_INTERNAL_REF(parent_call, "child");
     GPR_ASSERT(call->is_client);
@@ -323,10 +300,38 @@
 
     gpr_mu_unlock(&parent_call->mu);
   }
-  if (gpr_time_cmp(send_deadline, gpr_inf_future(send_deadline.clock_type)) !=
-      0) {
-    set_deadline_alarm(&exec_ctx, call, send_deadline);
+
+  call->send_deadline = send_deadline;
+
+  GRPC_CHANNEL_INTERNAL_REF(channel, "call");
+  /* initial refcount dropped by grpc_call_destroy */
+  grpc_error *error = grpc_call_stack_init(
+      &exec_ctx, channel_stack, 1, destroy_call, call, call->context,
+      server_transport_data, send_deadline, CALL_STACK_FROM_CALL(call));
+  if (error != GRPC_ERROR_NONE) {
+    grpc_status_code status;
+    const char *error_str;
+    grpc_error_get_status(error, &status, &error_str);
+    close_with_status(&exec_ctx, call, status, error_str);
+    GRPC_ERROR_UNREF(error);
   }
+  if (cq != NULL) {
+    GPR_ASSERT(
+        pollset_set_alternative == NULL &&
+        "Only one of 'cq' and 'pollset_set_alternative' should be non-NULL.");
+    GRPC_CQ_INTERNAL_REF(cq, "bind");
+    call->pollent =
+        grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq));
+  }
+  if (pollset_set_alternative != NULL) {
+    call->pollent =
+        grpc_polling_entity_create_from_pollset_set(pollset_set_alternative);
+  }
+  if (!grpc_polling_entity_is_empty(&call->pollent)) {
+    grpc_call_stack_set_pollset_or_pollset_set(
+        &exec_ctx, CALL_STACK_FROM_CALL(call), &call->pollent);
+  }
+
   grpc_exec_ctx_finish(&exec_ctx);
   GPR_TIMER_END("grpc_call_create", 0);
   return call;
@@ -361,6 +366,25 @@
   GRPC_CALL_STACK_UNREF(exec_ctx, CALL_STACK_FROM_CALL(c), REF_REASON);
 }
 
+static void get_final_status(grpc_call *call,
+                             void (*set_value)(grpc_status_code code,
+                                               void *user_data),
+                             void *set_value_user_data) {
+  int i;
+  for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
+    if (call->status[i].is_set) {
+      set_value(call->status[i].code, set_value_user_data);
+      return;
+    }
+  }
+  if (call->is_client) {
+    set_value(GRPC_STATUS_UNKNOWN, set_value_user_data);
+  } else {
+    set_value(GRPC_STATUS_OK, set_value_user_data);
+  }
+}
+
+static void set_status_value_directly(grpc_status_code status, void *dest);
 static void destroy_call(grpc_exec_ctx *exec_ctx, void *call,
                          grpc_error *error) {
   size_t i;
@@ -392,7 +416,11 @@
     GRPC_CQ_INTERNAL_UNREF(c->cq, "bind");
   }
   grpc_channel *channel = c->channel;
-  grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c), &c->stats, c);
+
+  get_final_status(call, set_status_value_directly,
+                   &c->final_info.final_status);
+
+  grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c), &c->final_info, c);
   GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, "call");
   GPR_TIMER_END("destroy_call", 0);
 }
@@ -414,40 +442,13 @@
   }
 }
 
-static void get_final_status(grpc_call *call,
-                             void (*set_value)(grpc_status_code code,
-                                               void *user_data),
-                             void *set_value_user_data) {
-  int i;
-  for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
-    if (call->status[i].is_set) {
-      set_value(call->status[i].code, set_value_user_data);
-      return;
-    }
-  }
-  if (call->is_client) {
-    set_value(GRPC_STATUS_UNKNOWN, set_value_user_data);
-  } else {
-    set_value(GRPC_STATUS_OK, set_value_user_data);
-  }
-}
-
 static void set_status_from_error(grpc_call *call, status_source source,
                                   grpc_error *error) {
-  intptr_t status;
-  if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &status)) {
-    set_status_code(call, source, (uint32_t)status);
-  } else {
-    set_status_code(call, source, GRPC_STATUS_INTERNAL);
-  }
-  const char *msg = grpc_error_get_str(error, GRPC_ERROR_STR_GRPC_MESSAGE);
-  bool free_msg = false;
-  if (msg == NULL) {
-    free_msg = true;
-    msg = grpc_error_string(error);
-  }
+  grpc_status_code status;
+  const char *msg;
+  grpc_error_get_status(error, &status, &msg);
+  set_status_code(call, source, (uint32_t)status);
   set_status_details(call, source, grpc_mdstr_from_string(msg));
-  if (free_msg) grpc_error_free_string(msg);
 }
 
 static void set_incoming_compression_algorithm(
@@ -720,9 +721,6 @@
   gpr_mu_lock(&c->mu);
   GPR_ASSERT(!c->destroy_called);
   c->destroy_called = 1;
-  if (c->have_alarm) {
-    grpc_timer_cancel(&exec_ctx, &c->alarm);
-  }
   cancel = !c->received_final_op;
   gpr_mu_unlock(&c->mu);
   if (cancel) grpc_call_cancel(c, NULL);
@@ -760,8 +758,8 @@
   grpc_closure closure;
   grpc_call *call;
   grpc_error *error;
-  grpc_closure *op_closure;
   enum { TC_CANCEL, TC_CLOSE } type;
+  grpc_transport_stream_op op;
 } termination_closure;
 
 static void done_termination(grpc_exec_ctx *exec_ctx, void *tcp,
@@ -776,31 +774,27 @@
       break;
   }
   GRPC_ERROR_UNREF(tc->error);
-  grpc_exec_ctx_sched(exec_ctx, tc->op_closure, GRPC_ERROR_NONE, NULL);
   gpr_free(tc);
 }
 
 static void send_cancel(grpc_exec_ctx *exec_ctx, void *tcp, grpc_error *error) {
-  grpc_transport_stream_op op;
   termination_closure *tc = tcp;
-  memset(&op, 0, sizeof(op));
-  op.cancel_error = tc->error;
+  memset(&tc->op, 0, sizeof(tc->op));
+  tc->op.cancel_error = tc->error;
   /* reuse closure to catch completion */
   grpc_closure_init(&tc->closure, done_termination, tc);
-  op.on_complete = &tc->closure;
-  execute_op(exec_ctx, tc->call, &op);
+  tc->op.on_complete = &tc->closure;
+  execute_op(exec_ctx, tc->call, &tc->op);
 }
 
 static void send_close(grpc_exec_ctx *exec_ctx, void *tcp, grpc_error *error) {
-  grpc_transport_stream_op op;
   termination_closure *tc = tcp;
-  memset(&op, 0, sizeof(op));
-  op.close_error = tc->error;
+  memset(&tc->op, 0, sizeof(tc->op));
+  tc->op.close_error = tc->error;
   /* reuse closure to catch completion */
   grpc_closure_init(&tc->closure, done_termination, tc);
-  tc->op_closure = op.on_complete;
-  op.on_complete = &tc->closure;
-  execute_op(exec_ctx, tc->call, &op);
+  tc->op.on_complete = &tc->closure;
+  execute_op(exec_ctx, tc->call, &tc->op);
 }
 
 static grpc_call_error terminate_with_status(grpc_exec_ctx *exec_ctx,
@@ -881,32 +875,6 @@
   return CALL_FROM_TOP_ELEM(elem);
 }
 
-static void call_alarm(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
-  grpc_call *call = arg;
-  gpr_mu_lock(&call->mu);
-  call->have_alarm = 0;
-  if (error != GRPC_ERROR_CANCELLED) {
-    cancel_with_status(exec_ctx, call, GRPC_STATUS_DEADLINE_EXCEEDED,
-                       "Deadline Exceeded");
-  }
-  gpr_mu_unlock(&call->mu);
-  GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "alarm");
-}
-
-static void set_deadline_alarm(grpc_exec_ctx *exec_ctx, grpc_call *call,
-                               gpr_timespec deadline) {
-  if (call->have_alarm) {
-    gpr_log(GPR_ERROR, "Attempt to set deadline alarm twice");
-    assert(0);
-    return;
-  }
-  GRPC_CALL_INTERNAL_REF(call, "alarm");
-  call->have_alarm = 1;
-  call->send_deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
-  grpc_timer_init(exec_ctx, &call->alarm, call->send_deadline, call_alarm, call,
-                  gpr_now(GPR_CLOCK_MONOTONIC));
-}
-
 /* we offset status by a small amount when storing it into transport metadata
    as metadata cannot store a 0 value (which is used as OK for grpc_status_codes
    */
@@ -1144,17 +1112,6 @@
     if (gpr_unref(&bctl->steps_to_complete)) {
       post_batch_completion(exec_ctx, bctl);
     }
-  } else if (call->receiving_stream->length >
-             grpc_channel_get_max_message_length(call->channel)) {
-    cancel_with_status(exec_ctx, call, GRPC_STATUS_INTERNAL,
-                       "Max message size exceeded");
-    grpc_byte_stream_destroy(exec_ctx, call->receiving_stream);
-    call->receiving_stream = NULL;
-    *call->receiving_buffer = NULL;
-    call->receiving_message = 0;
-    if (gpr_unref(&bctl->steps_to_complete)) {
-      post_batch_completion(exec_ctx, bctl);
-    }
   } else {
     call->test_only_last_message_flags = call->receiving_stream->flags;
     if ((call->receiving_stream->flags & GRPC_WRITE_INTERNAL_COMPRESS) &&
@@ -1259,9 +1216,8 @@
     if (gpr_time_cmp(md->deadline, gpr_inf_future(md->deadline.clock_type)) !=
             0 &&
         !call->is_client) {
-      GPR_TIMER_BEGIN("set_deadline_alarm", 0);
-      set_deadline_alarm(exec_ctx, call, md->deadline);
-      GPR_TIMER_END("set_deadline_alarm", 0);
+      call->send_deadline =
+          gpr_convert_clock_type(md->deadline, GPR_CLOCK_MONOTONIC);
     }
   }
 
@@ -1290,9 +1246,17 @@
   GRPC_ERROR_REF(error);
 
   gpr_mu_lock(&call->mu);
+
+  // If the error has an associated status code, set the call's status.
+  intptr_t status;
+  if (error != GRPC_ERROR_NONE &&
+      grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &status)) {
+    set_status_from_error(call, STATUS_FROM_CORE, error);
+  }
+
   if (bctl->send_initial_metadata) {
     if (error != GRPC_ERROR_NONE) {
-      set_status_code(call, STATUS_FROM_CORE, GRPC_STATUS_UNAVAILABLE);
+      set_status_from_error(call, STATUS_FROM_CORE, error);
     }
     grpc_metadata_batch_destroy(
         &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]);
@@ -1310,9 +1274,6 @@
     grpc_metadata_batch_filter(md, recv_trailing_filter, call);
 
     call->received_final_op = true;
-    if (call->have_alarm) {
-      grpc_timer_cancel(exec_ctx, &call->alarm);
-    }
     /* propagate cancellation to any interested children */
     child_call = call->first_child;
     if (child_call != NULL) {
@@ -1354,19 +1315,19 @@
                                         grpc_call *call, const grpc_op *ops,
                                         size_t nops, void *notify_tag,
                                         int is_notify_tag_closure) {
-  grpc_transport_stream_op stream_op;
   size_t i;
   const grpc_op *op;
   batch_control *bctl;
   int num_completion_callbacks_needed = 1;
   grpc_call_error error = GRPC_CALL_OK;
 
+  // sent_initial_metadata guards against variable reuse.
+  grpc_metadata compression_md;
+
   GPR_TIMER_BEGIN("grpc_call_start_batch", 0);
 
   GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, notify_tag);
 
-  memset(&stream_op, 0, sizeof(stream_op));
-
   /* TODO(ctiller): this feels like it could be made lock-free */
   gpr_mu_lock(&call->mu);
   bctl = allocate_batch_control(call);
@@ -1375,6 +1336,9 @@
   bctl->notify_tag = notify_tag;
   bctl->is_notify_tag_closure = (uint8_t)(is_notify_tag_closure != 0);
 
+  grpc_transport_stream_op *stream_op = &bctl->op;
+  memset(stream_op, 0, sizeof(*stream_op));
+
   if (nops == 0) {
     GRPC_CALL_INTERNAL_REF(call, "completion");
     bctl->error = GRPC_ERROR_NONE;
@@ -1406,8 +1370,7 @@
           goto done_with_error;
         }
         /* process compression level */
-        grpc_metadata compression_md;
-        memset(&compression_md, 0, sizeof(grpc_metadata));
+        memset(&compression_md, 0, sizeof(compression_md));
         size_t additional_metadata_count = 0;
         grpc_compression_level effective_compression_level;
         bool level_set = false;
@@ -1453,9 +1416,9 @@
         }
         /* TODO(ctiller): just make these the same variable? */
         call->metadata_batch[0][0].deadline = call->send_deadline;
-        stream_op.send_initial_metadata =
+        stream_op->send_initial_metadata =
             &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */];
-        stream_op.send_initial_metadata_flags = op->flags;
+        stream_op->send_initial_metadata_flags = op->flags;
         break;
       case GRPC_OP_SEND_MESSAGE:
         if (!are_write_flags_valid(op->flags)) {
@@ -1475,7 +1438,7 @@
         grpc_slice_buffer_stream_init(
             &call->sending_stream,
             &op->data.send_message->data.raw.slice_buffer, op->flags);
-        stream_op.send_message = &call->sending_stream.base;
+        stream_op->send_message = &call->sending_stream.base;
         break;
       case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
         /* Flag validation: currently allow no flags */
@@ -1493,7 +1456,7 @@
         }
         bctl->send_final_op = 1;
         call->sent_final_op = 1;
-        stream_op.send_trailing_metadata =
+        stream_op->send_trailing_metadata =
             &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */];
         break;
       case GRPC_OP_SEND_STATUS_FROM_SERVER:
@@ -1540,7 +1503,7 @@
           error = GRPC_CALL_ERROR_INVALID_METADATA;
           goto done_with_error;
         }
-        stream_op.send_trailing_metadata =
+        stream_op->send_trailing_metadata =
             &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */];
         break;
       case GRPC_OP_RECV_INITIAL_METADATA:
@@ -1558,9 +1521,9 @@
         grpc_closure_init(&call->receiving_initial_metadata_ready,
                           receiving_initial_metadata_ready, bctl);
         bctl->recv_initial_metadata = 1;
-        stream_op.recv_initial_metadata =
+        stream_op->recv_initial_metadata =
             &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
-        stream_op.recv_initial_metadata_ready =
+        stream_op->recv_initial_metadata_ready =
             &call->receiving_initial_metadata_ready;
         num_completion_callbacks_needed++;
         break;
@@ -1577,10 +1540,10 @@
         call->receiving_message = 1;
         bctl->recv_message = 1;
         call->receiving_buffer = op->data.recv_message;
-        stream_op.recv_message = &call->receiving_stream;
+        stream_op->recv_message = &call->receiving_stream;
         grpc_closure_init(&call->receiving_stream_ready, receiving_stream_ready,
                           bctl);
-        stream_op.recv_message_ready = &call->receiving_stream_ready;
+        stream_op->recv_message_ready = &call->receiving_stream_ready;
         num_completion_callbacks_needed++;
         break;
       case GRPC_OP_RECV_STATUS_ON_CLIENT:
@@ -1606,9 +1569,10 @@
         call->final_op.client.status_details_capacity =
             op->data.recv_status_on_client.status_details_capacity;
         bctl->recv_final_op = 1;
-        stream_op.recv_trailing_metadata =
+        stream_op->recv_trailing_metadata =
             &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
-        stream_op.collect_stats = &call->stats.transport_stream_stats;
+        stream_op->collect_stats =
+            &call->final_info.stats.transport_stream_stats;
         break;
       case GRPC_OP_RECV_CLOSE_ON_SERVER:
         /* Flag validation: currently allow no flags */
@@ -1628,9 +1592,10 @@
         call->final_op.server.cancelled =
             op->data.recv_close_on_server.cancelled;
         bctl->recv_final_op = 1;
-        stream_op.recv_trailing_metadata =
+        stream_op->recv_trailing_metadata =
             &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
-        stream_op.collect_stats = &call->stats.transport_stream_stats;
+        stream_op->collect_stats =
+            &call->final_info.stats.transport_stream_stats;
         break;
     }
   }
@@ -1641,12 +1606,12 @@
   }
   gpr_ref_init(&bctl->steps_to_complete, num_completion_callbacks_needed);
 
-  stream_op.context = call->context;
+  stream_op->context = call->context;
   grpc_closure_init(&bctl->finish_batch, finish_batch, bctl);
-  stream_op.on_complete = &bctl->finish_batch;
+  stream_op->on_complete = &bctl->finish_batch;
   gpr_mu_unlock(&call->mu);
 
-  execute_op(exec_ctx, call, &stream_op);
+  execute_op(exec_ctx, call, stream_op);
 
 done:
   GPR_TIMER_END("grpc_call_start_batch", 0);
diff --git a/src/core/lib/surface/channel.c b/src/core/lib/surface/channel.c
index 6d2b1c4..6adb70a 100644
--- a/src/core/lib/surface/channel.c
+++ b/src/core/lib/surface/channel.c
@@ -64,7 +64,6 @@
 
 struct grpc_channel {
   int is_client;
-  uint32_t max_message_length;
   grpc_compression_options compression_options;
   grpc_mdelem *default_authority;
 
@@ -80,9 +79,6 @@
 #define CHANNEL_FROM_TOP_ELEM(top_elem) \
   CHANNEL_FROM_CHANNEL_STACK(grpc_channel_stack_from_top_element(top_elem))
 
-/* the protobuf library will (by default) start warning at 100megs */
-#define DEFAULT_MAX_MESSAGE_LENGTH (4 * 1024 * 1024)
-
 static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg,
                             grpc_error *error);
 
@@ -114,21 +110,10 @@
   gpr_mu_init(&channel->registered_call_mu);
   channel->registered_calls = NULL;
 
-  channel->max_message_length = DEFAULT_MAX_MESSAGE_LENGTH;
   grpc_compression_options_init(&channel->compression_options);
   if (args) {
     for (size_t i = 0; i < args->num_args; i++) {
-      if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_MESSAGE_LENGTH)) {
-        if (args->args[i].type != GRPC_ARG_INTEGER) {
-          gpr_log(GPR_ERROR, "%s ignored: it must be an integer",
-                  GRPC_ARG_MAX_MESSAGE_LENGTH);
-        } else if (args->args[i].value.integer < 0) {
-          gpr_log(GPR_ERROR, "%s ignored: it must be >= 0",
-                  GRPC_ARG_MAX_MESSAGE_LENGTH);
-        } else {
-          channel->max_message_length = (uint32_t)args->args[i].value.integer;
-        }
-      } else if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) {
+      if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) {
         if (args->args[i].type != GRPC_ARG_STRING) {
           gpr_log(GPR_ERROR, "%s ignored: it must be a string",
                   GRPC_ARG_DEFAULT_AUTHORITY);
@@ -334,14 +319,13 @@
 }
 
 void grpc_channel_destroy(grpc_channel *channel) {
-  grpc_transport_op op;
+  grpc_transport_op *op = grpc_make_transport_op(NULL);
   grpc_channel_element *elem;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   GRPC_API_TRACE("grpc_channel_destroy(channel=%p)", 1, (channel));
-  memset(&op, 0, sizeof(op));
-  op.disconnect_with_error = GRPC_ERROR_CREATE("Channel Destroyed");
+  op->disconnect_with_error = GRPC_ERROR_CREATE("Channel Destroyed");
   elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0);
-  elem->filter->start_transport_op(&exec_ctx, elem, &op);
+  elem->filter->start_transport_op(&exec_ctx, elem, op);
 
   GRPC_CHANNEL_INTERNAL_UNREF(&exec_ctx, channel, "channel");
 
@@ -371,7 +355,3 @@
   return grpc_mdelem_from_metadata_strings(GRPC_MDSTR_GRPC_STATUS,
                                            grpc_mdstr_from_string(tmp));
 }
-
-uint32_t grpc_channel_get_max_message_length(grpc_channel *channel) {
-  return channel->max_message_length;
-}
diff --git a/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h
index 7eff7b8..23cc865 100644
--- a/src/core/lib/surface/channel.h
+++ b/src/core/lib/surface/channel.h
@@ -42,6 +42,14 @@
                                   grpc_channel_stack_type channel_stack_type,
                                   grpc_transport *optional_transport);
 
+/** Create a call given a grpc_channel, in order to call \a method.
+    Progress is tied to activity on \a pollset_set. The returned call object is
+    meant to be used with \a grpc_call_start_batch_and_execute, which relies on
+    callbacks to signal completions. \a method and \a host need
+    only live through the invocation of this function. If \a parent_call is
+    non-NULL, it must be a server-side call. It will be used to propagate
+    properties from the server call to this new client call, depending on the
+    value of \a propagation_mask (see propagation_bits.h for possible values) */
 grpc_call *grpc_channel_create_pollset_set_call(
     grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask,
     grpc_pollset_set *pollset_set, const char *method, const char *host,
@@ -56,7 +64,6 @@
     The returned elem is owned by the caller. */
 grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel,
                                                  int status_code);
-uint32_t grpc_channel_get_max_message_length(grpc_channel *channel);
 
 #ifdef GRPC_STREAM_REFCOUNT_DEBUG
 void grpc_channel_internal_ref(grpc_channel *channel, const char *reason);
diff --git a/src/core/lib/surface/channel_init.h b/src/core/lib/surface/channel_init.h
index 3a18a61..b53f2ae 100644
--- a/src/core/lib/surface/channel_init.h
+++ b/src/core/lib/surface/channel_init.h
@@ -40,6 +40,10 @@
 
 #define GRPC_CHANNEL_INIT_BUILTIN_PRIORITY 10000
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /// This module provides a way for plugins (and the grpc core library itself)
 /// to register mutators for channel stacks.
 /// It also provides a universal entry path to run those mutators to build
@@ -84,4 +88,8 @@
                                     grpc_channel_stack_builder *builder,
                                     grpc_channel_stack_type type);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H */
diff --git a/src/core/lib/surface/channel_ping.c b/src/core/lib/surface/channel_ping.c
index 9818f9d..0d2f01a 100644
--- a/src/core/lib/surface/channel_ping.c
+++ b/src/core/lib/surface/channel_ping.c
@@ -61,19 +61,20 @@
 
 void grpc_channel_ping(grpc_channel *channel, grpc_completion_queue *cq,
                        void *tag, void *reserved) {
-  grpc_transport_op op;
+  GRPC_API_TRACE("grpc_channel_ping(channel=%p, cq=%p, tag=%p, reserved=%p)", 4,
+                 (channel, cq, tag, reserved));
+  grpc_transport_op *op = grpc_make_transport_op(NULL);
   ping_result *pr = gpr_malloc(sizeof(*pr));
   grpc_channel_element *top_elem =
       grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   GPR_ASSERT(reserved == NULL);
-  memset(&op, 0, sizeof(op));
   pr->tag = tag;
   pr->cq = cq;
   grpc_closure_init(&pr->closure, ping_done, pr);
-  op.send_ping = &pr->closure;
-  op.bind_pollset = grpc_cq_pollset(cq);
+  op->send_ping = &pr->closure;
+  op->bind_pollset = grpc_cq_pollset(cq);
   grpc_cq_begin_op(cq, tag);
-  top_elem->filter->start_transport_op(&exec_ctx, top_elem, &op);
+  top_elem->filter->start_transport_op(&exec_ctx, top_elem, op);
   grpc_exec_ctx_finish(&exec_ctx);
 }
diff --git a/src/core/lib/surface/completion_queue.h b/src/core/lib/surface/completion_queue.h
index 3049284..4dbf3aa 100644
--- a/src/core/lib/surface/completion_queue.h
+++ b/src/core/lib/surface/completion_queue.h
@@ -57,6 +57,8 @@
   uintptr_t next;
 } grpc_cq_completion;
 
+//#define GRPC_CQ_REF_COUNT_DEBUG
+
 #ifdef GRPC_CQ_REF_COUNT_DEBUG
 void grpc_cq_internal_ref(grpc_completion_queue *cc, const char *reason,
                           const char *file, int line);
diff --git a/src/core/lib/surface/init.c b/src/core/lib/surface/init.c
index 5397913..289f4ce 100644
--- a/src/core/lib/surface/init.c
+++ b/src/core/lib/surface/init.c
@@ -43,10 +43,13 @@
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/channel/compress_filter.h"
 #include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/channel/deadline_filter.h"
 #include "src/core/lib/channel/http_client_filter.h"
 #include "src/core/lib/channel/http_server_filter.h"
+#include "src/core/lib/channel/message_size_filter.h"
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/profiling/timers.h"
@@ -98,6 +101,21 @@
 
 static void register_builtin_channel_init() {
   grpc_channel_init_register_stage(
+      GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+      prepend_filter, (void *)&grpc_client_deadline_filter);
+  grpc_channel_init_register_stage(
+      GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, prepend_filter,
+      (void *)&grpc_server_deadline_filter);
+  grpc_channel_init_register_stage(
+      GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+      prepend_filter, (void *)&grpc_message_size_filter);
+  grpc_channel_init_register_stage(
+      GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+      prepend_filter, (void *)&grpc_message_size_filter);
+  grpc_channel_init_register_stage(
+      GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, prepend_filter,
+      (void *)&grpc_message_size_filter);
+  grpc_channel_init_register_stage(
       GRPC_CLIENT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, prepend_filter,
       (void *)&grpc_compress_filter);
   grpc_channel_init_register_stage(
@@ -165,6 +183,7 @@
     grpc_register_tracer("http1", &grpc_http1_trace);
     grpc_register_tracer("compression", &grpc_compression_trace);
     grpc_register_tracer("queue_pluck", &grpc_cq_pluck_trace);
+    grpc_register_tracer("combiner", &grpc_combiner_trace);
     // Default pluck trace to 1
     grpc_cq_pluck_trace = 1;
     grpc_register_tracer("queue_timeout", &grpc_cq_event_timeout_trace);
diff --git a/src/core/lib/surface/lame_client.c b/src/core/lib/surface/lame_client.c
index 5ea4cba..d32c884 100644
--- a/src/core/lib/surface/lame_client.c
+++ b/src/core/lib/surface/lame_client.c
@@ -97,21 +97,24 @@
     grpc_exec_ctx_sched(exec_ctx, op->on_connectivity_state_change,
                         GRPC_ERROR_NONE, NULL);
   }
-  if (op->on_consumed != NULL) {
-    grpc_exec_ctx_sched(exec_ctx, op->on_consumed, GRPC_ERROR_NONE, NULL);
-  }
   if (op->send_ping != NULL) {
     grpc_exec_ctx_sched(exec_ctx, op->send_ping,
                         GRPC_ERROR_CREATE("lame client channel"), NULL);
   }
   GRPC_ERROR_UNREF(op->disconnect_with_error);
+  if (op->on_consumed != NULL) {
+    grpc_exec_ctx_sched(exec_ctx, op->on_consumed, GRPC_ERROR_NONE, NULL);
+  }
 }
 
-static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {}
+static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  grpc_call_element_args *args) {
+  return GRPC_ERROR_NONE;
+}
 
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats,
+                              const grpc_call_final_info *final_info,
                               void *and_free_memory) {
   gpr_free(and_free_memory);
 }
diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c
index 2f108af..7300d79 100644
--- a/src/core/lib/surface/server.c
+++ b/src/core/lib/surface/server.c
@@ -149,6 +149,7 @@
 
   grpc_metadata_batch *recv_initial_metadata;
   bool recv_idempotent_request;
+  bool recv_cacheable_request;
   grpc_metadata_array initial_metadata;
 
   request_matcher *request_matcher;
@@ -273,27 +274,24 @@
 
 static void send_shutdown(grpc_exec_ctx *exec_ctx, grpc_channel *channel,
                           int send_goaway, grpc_error *send_disconnect) {
-  grpc_transport_op op;
-  struct shutdown_cleanup_args *sc;
+  struct shutdown_cleanup_args *sc = gpr_malloc(sizeof(*sc));
+  grpc_closure_init(&sc->closure, shutdown_cleanup, sc);
+  grpc_transport_op *op = grpc_make_transport_op(&sc->closure);
   grpc_channel_element *elem;
 
-  memset(&op, 0, sizeof(op));
-  op.send_goaway = send_goaway;
-  sc = gpr_malloc(sizeof(*sc));
+  op->send_goaway = send_goaway;
   sc->slice = gpr_slice_from_copied_string("Server shutdown");
-  op.goaway_message = &sc->slice;
-  op.goaway_status = GRPC_STATUS_OK;
-  op.disconnect_with_error = send_disconnect;
-  grpc_closure_init(&sc->closure, shutdown_cleanup, sc);
-  op.on_consumed = &sc->closure;
+  op->goaway_message = &sc->slice;
+  op->goaway_status = GRPC_STATUS_OK;
+  op->disconnect_with_error = send_disconnect;
 
   elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
-  elem->filter->start_transport_op(exec_ctx, elem, &op);
+  elem->filter->start_transport_op(exec_ctx, elem, op);
 }
 
 static void channel_broadcaster_shutdown(grpc_exec_ctx *exec_ctx,
                                          channel_broadcaster *cb,
-                                         int send_goaway,
+                                         bool send_goaway,
                                          grpc_error *force_disconnect) {
   size_t i;
 
@@ -431,7 +429,8 @@
   server_unref(exec_ctx, server);
 }
 
-static void destroy_channel(grpc_exec_ctx *exec_ctx, channel_data *chand) {
+static void destroy_channel(grpc_exec_ctx *exec_ctx, channel_data *chand,
+                            grpc_error *error) {
   if (is_channel_orphaned(chand)) return;
   GPR_ASSERT(chand->server != NULL);
   orphan_channel(chand);
@@ -440,14 +439,20 @@
   chand->finish_destroy_channel_closure.cb = finish_destroy_channel;
   chand->finish_destroy_channel_closure.cb_arg = chand;
 
-  grpc_transport_op op;
-  memset(&op, 0, sizeof(op));
-  op.set_accept_stream = true;
-  op.on_consumed = &chand->finish_destroy_channel_closure;
+  grpc_transport_op *op =
+      grpc_make_transport_op(&chand->finish_destroy_channel_closure);
+  op->set_accept_stream = true;
   grpc_channel_next_op(exec_ctx,
                        grpc_channel_stack_element(
                            grpc_channel_get_channel_stack(chand->channel), 0),
-                       &op);
+                       op);
+
+  if (error != GRPC_ERROR_NONE) {
+    const char *msg = grpc_error_string(error);
+    gpr_log(GPR_INFO, "Disconnected client: %s", msg);
+    grpc_error_free_string(msg);
+  }
+  GRPC_ERROR_UNREF(error);
 }
 
 static void cpstr(char **dest, size_t *capacity, grpc_mdstr *value) {
@@ -497,9 +502,12 @@
             &rc->data.batch.details->method_capacity, calld->path);
       rc->data.batch.details->deadline = calld->deadline;
       rc->data.batch.details->flags =
-          0 | (calld->recv_idempotent_request
-                   ? GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST
-                   : 0);
+          (calld->recv_idempotent_request
+               ? GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST
+               : 0) |
+          (calld->recv_cacheable_request
+               ? GRPC_INITIAL_METADATA_CACHEABLE_REQUEST
+               : 0);
       break;
     case REGISTERED_CALL:
       *rc->data.registered.deadline = calld->deadline;
@@ -779,6 +787,7 @@
     calld->on_done_recv_initial_metadata = op->recv_initial_metadata_ready;
     op->recv_initial_metadata_ready = &calld->server_on_recv_initial_metadata;
     op->recv_idempotent_request = &calld->recv_idempotent_request;
+    op->recv_cacheable_request = &calld->recv_cacheable_request;
   }
 }
 
@@ -840,24 +849,24 @@
   channel_data *chand = cd;
   grpc_server *server = chand->server;
   if (chand->connectivity_state != GRPC_CHANNEL_SHUTDOWN) {
-    grpc_transport_op op;
-    memset(&op, 0, sizeof(op));
-    op.on_connectivity_state_change = &chand->channel_connectivity_changed,
-    op.connectivity_state = &chand->connectivity_state;
+    grpc_transport_op *op = grpc_make_transport_op(NULL);
+    op->on_connectivity_state_change = &chand->channel_connectivity_changed,
+    op->connectivity_state = &chand->connectivity_state;
     grpc_channel_next_op(exec_ctx,
                          grpc_channel_stack_element(
                              grpc_channel_get_channel_stack(chand->channel), 0),
-                         &op);
+                         op);
   } else {
     gpr_mu_lock(&server->mu_global);
-    destroy_channel(exec_ctx, chand);
+    destroy_channel(exec_ctx, chand, GRPC_ERROR_REF(error));
     gpr_mu_unlock(&server->mu_global);
     GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->channel, "connectivity");
   }
 }
 
-static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {
+static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  grpc_call_element_args *args) {
   call_data *calld = elem->call_data;
   channel_data *chand = elem->channel_data;
   memset(calld, 0, sizeof(call_data));
@@ -869,10 +878,12 @@
                     server_on_recv_initial_metadata, elem);
 
   server_ref(chand->server);
+  return GRPC_ERROR_NONE;
 }
 
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {
   channel_data *chand = elem->channel_data;
   call_data *calld = elem->call_data;
 
@@ -1095,6 +1106,12 @@
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
+void grpc_server_get_pollsets(grpc_server *server, grpc_pollset ***pollsets,
+                              size_t *pollset_count) {
+  *pollset_count = server->cq_count;
+  *pollsets = server->pollsets;
+}
+
 void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *s,
                                  grpc_transport *transport,
                                  grpc_pollset *accepting_pollset,
@@ -1111,7 +1128,7 @@
   size_t slots;
   uint32_t probes;
   uint32_t max_probes = 0;
-  grpc_transport_op op;
+  grpc_transport_op *op = NULL;
 
   channel =
       grpc_channel_create(exec_ctx, NULL, args, GRPC_SERVER_CHANNEL, transport);
@@ -1171,16 +1188,16 @@
   gpr_mu_unlock(&s->mu_global);
 
   GRPC_CHANNEL_INTERNAL_REF(channel, "connectivity");
-  memset(&op, 0, sizeof(op));
-  op.set_accept_stream = true;
-  op.set_accept_stream_fn = accept_stream;
-  op.set_accept_stream_user_data = chand;
-  op.on_connectivity_state_change = &chand->channel_connectivity_changed;
-  op.connectivity_state = &chand->connectivity_state;
+  op = grpc_make_transport_op(NULL);
+  op->set_accept_stream = true;
+  op->set_accept_stream_fn = accept_stream;
+  op->set_accept_stream_user_data = chand;
+  op->on_connectivity_state_change = &chand->channel_connectivity_changed;
+  op->connectivity_state = &chand->connectivity_state;
   if (gpr_atm_acq_load(&s->shutdown_flag) != 0) {
-    op.disconnect_with_error = GRPC_ERROR_CREATE("Server shutdown");
+    op->disconnect_with_error = GRPC_ERROR_CREATE("Server shutdown");
   }
-  grpc_transport_perform_op(exec_ctx, transport, &op);
+  grpc_transport_perform_op(exec_ctx, transport, op);
 }
 
 void done_published_shutdown(grpc_exec_ctx *exec_ctx, void *done_arg,
@@ -1249,7 +1266,8 @@
     l->destroy(&exec_ctx, server, l->arg, &l->destroy_done);
   }
 
-  channel_broadcaster_shutdown(&exec_ctx, &broadcaster, 1, 0);
+  channel_broadcaster_shutdown(&exec_ctx, &broadcaster, true /* send_goaway */,
+                               GRPC_ERROR_NONE);
 
 done:
   grpc_exec_ctx_finish(&exec_ctx);
@@ -1265,7 +1283,7 @@
   channel_broadcaster_init(server, &broadcaster);
   gpr_mu_unlock(&server->mu_global);
 
-  channel_broadcaster_shutdown(&exec_ctx, &broadcaster, 0,
+  channel_broadcaster_shutdown(&exec_ctx, &broadcaster, false /* send_goaway */,
                                GRPC_ERROR_CREATE("Cancelling all calls"));
   grpc_exec_ctx_finish(&exec_ctx);
 }
diff --git a/src/core/lib/surface/server.h b/src/core/lib/surface/server.h
index fb6e4d6..551a40a 100644
--- a/src/core/lib/surface/server.h
+++ b/src/core/lib/surface/server.h
@@ -60,4 +60,9 @@
 
 int grpc_server_has_open_connections(grpc_server *server);
 
+/* Do not call this before grpc_server_start. Returns the pollsets and the
+ * number of pollsets via 'pollsets' and 'pollset_count'. */
+void grpc_server_get_pollsets(grpc_server *server, grpc_pollset ***pollsets,
+                              size_t *pollset_count);
+
 #endif /* GRPC_CORE_LIB_SURFACE_SERVER_H */
diff --git a/src/core/lib/surface/version.c b/src/core/lib/surface/version.c
index 1942075..4124268 100644
--- a/src/core/lib/surface/version.c
+++ b/src/core/lib/surface/version.c
@@ -37,3 +37,5 @@
 #include <grpc/grpc.h>
 
 const char *grpc_version_string(void) { return "1.1.0-dev"; }
+
+const char *grpc_g_stands_for(void) { return "good"; }
diff --git a/src/core/lib/transport/byte_stream.h b/src/core/lib/transport/byte_stream.h
index 95519a9..e64dce6 100644
--- a/src/core/lib/transport/byte_stream.h
+++ b/src/core/lib/transport/byte_stream.h
@@ -59,13 +59,9 @@
  * on_complete will not be called), 0 if the bytes will be available
  * asynchronously.
  *
- * on entry, *remaining can be set as a hint as to the maximum number
+ * max_size_hint can be set as a hint as to the maximum number
  * of bytes that would be acceptable to read.
  *
- * fills *buffer, *length, *remaining with the bytes, length of bytes
- * and length of data remaining to be read before either returning 1
- * or calling on_complete.
- *
  * once a slice is returned into *slice, it is owned by the caller.
  */
 int grpc_byte_stream_next(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/lib/transport/metadata.c b/src/core/lib/transport/metadata.c
index 0677f29..4b40c27 100644
--- a/src/core/lib/transport/metadata.c
+++ b/src/core/lib/transport/metadata.c
@@ -278,7 +278,7 @@
                           internal_metadata *md DEBUG_ARGS) {
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
   gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-          "ELM   REF:%p:%d->%d: '%s' = '%s'", md,
+          "ELM   REF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
           gpr_atm_no_barrier_load(&md->refcnt),
           gpr_atm_no_barrier_load(&md->refcnt) + 1,
           grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
@@ -566,7 +566,7 @@
   shard->elems[idx] = md;
   gpr_mu_init(&md->mu_user_data);
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
-  gpr_log(GPR_DEBUG, "ELM   NEW:%p:%d: '%s' = '%s'", md,
+  gpr_log(GPR_DEBUG, "ELM   NEW:%p:%zu: '%s' = '%s'", (void *)md,
           gpr_atm_no_barrier_load(&md->refcnt),
           grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
           grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
@@ -639,7 +639,7 @@
   if (is_mdelem_static(gmd)) return gmd;
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
   gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-          "ELM   REF:%p:%d->%d: '%s' = '%s'", md,
+          "ELM   REF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
           gpr_atm_no_barrier_load(&md->refcnt),
           gpr_atm_no_barrier_load(&md->refcnt) + 1,
           grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
@@ -649,7 +649,7 @@
      this function - meaning that no adjustment to mdtab_free is necessary,
      simplifying the logic here to be just an atomic increment */
   /* use C assert to have this removed in opt builds */
-  assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
+  GPR_ASSERT(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
   gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
   return gmd;
 }
@@ -660,14 +660,16 @@
   if (is_mdelem_static(gmd)) return;
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
   gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-          "ELM UNREF:%p:%d->%d: '%s' = '%s'", md,
+          "ELM UNREF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
           gpr_atm_no_barrier_load(&md->refcnt),
           gpr_atm_no_barrier_load(&md->refcnt) - 1,
           grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
           grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
 #endif
   uint32_t hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
-  if (1 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
+  const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
+  GPR_ASSERT(prev_refcount >= 1);
+  if (1 == prev_refcount) {
     /* once the refcount hits zero, some other thread can come along and
        free md at any time: it's unsafe from this point on to access it */
     mdtab_shard *shard =
@@ -676,10 +678,12 @@
   }
 }
 
-const char *grpc_mdstr_as_c_string(grpc_mdstr *s) {
+const char *grpc_mdstr_as_c_string(const grpc_mdstr *s) {
   return (const char *)GPR_SLICE_START_PTR(s->slice);
 }
 
+size_t grpc_mdstr_length(const grpc_mdstr *s) { return GRPC_MDSTR_LENGTH(s); }
+
 grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
   internal_string *s = (internal_string *)gs;
   if (is_mdstr_static(gs)) return gs;
diff --git a/src/core/lib/transport/metadata.h b/src/core/lib/transport/metadata.h
index 6d82f4d..71eff0a 100644
--- a/src/core/lib/transport/metadata.h
+++ b/src/core/lib/transport/metadata.h
@@ -37,6 +37,10 @@
 #include <grpc/support/slice.h>
 #include <grpc/support/useful.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* This file provides a mechanism for tracking metadata through the grpc stack.
    It's not intended for consumption outside of the library.
 
@@ -143,7 +147,7 @@
 
 /* Recover a char* from a grpc_mdstr. The returned string is null terminated.
    Does not promise that the returned string has no embedded nulls however. */
-const char *grpc_mdstr_as_c_string(grpc_mdstr *s);
+const char *grpc_mdstr_as_c_string(const grpc_mdstr *s);
 
 #define GRPC_MDSTR_LENGTH(s) (GPR_SLICE_LENGTH(s->slice))
 
@@ -164,4 +168,8 @@
 extern gpr_slice (*grpc_chttp2_base64_encode_and_huffman_compress)(
     gpr_slice input);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* GRPC_CORE_LIB_TRANSPORT_METADATA_H */
diff --git a/src/core/lib/transport/metadata_batch.c b/src/core/lib/transport/metadata_batch.c
index e4398ab..84b5a74 100644
--- a/src/core/lib/transport/metadata_batch.c
+++ b/src/core/lib/transport/metadata_batch.c
@@ -33,6 +33,7 @@
 
 #include "src/core/lib/transport/metadata_batch.h"
 
+#include <stdbool.h>
 #include <string.h>
 
 #include <grpc/support/alloc.h>
@@ -187,7 +188,7 @@
   grpc_metadata_batch_filter(batch, no_metadata_for_you, NULL);
 }
 
-int grpc_metadata_batch_is_empty(grpc_metadata_batch *batch) {
+bool grpc_metadata_batch_is_empty(grpc_metadata_batch *batch) {
   return batch->list.head == NULL &&
          gpr_time_cmp(gpr_inf_future(batch->deadline.clock_type),
                       batch->deadline) == 0;
diff --git a/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h
index 7af823f..0424b4d 100644
--- a/src/core/lib/transport/metadata_batch.h
+++ b/src/core/lib/transport/metadata_batch.h
@@ -34,12 +34,18 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H
 #define GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H
 
+#include <stdbool.h>
+
 #include <grpc/grpc.h>
 #include <grpc/support/port_platform.h>
 #include <grpc/support/slice.h>
 #include <grpc/support/time.h>
 #include "src/core/lib/transport/metadata.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 typedef struct grpc_linked_mdelem {
   grpc_mdelem *md;
   struct grpc_linked_mdelem *next;
@@ -64,7 +70,7 @@
 void grpc_metadata_batch_init(grpc_metadata_batch *batch);
 void grpc_metadata_batch_destroy(grpc_metadata_batch *batch);
 void grpc_metadata_batch_clear(grpc_metadata_batch *batch);
-int grpc_metadata_batch_is_empty(grpc_metadata_batch *batch);
+bool grpc_metadata_batch_is_empty(grpc_metadata_batch *batch);
 
 /* Returns the transport size of the batch. */
 size_t grpc_metadata_batch_size(grpc_metadata_batch *batch);
@@ -125,4 +131,8 @@
   } while (0)
 #endif
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H */
diff --git a/src/core/lib/transport/static_metadata.c b/src/core/lib/transport/static_metadata.c
index c5f16e5..5e0352a 100644
--- a/src/core/lib/transport/static_metadata.c
+++ b/src/core/lib/transport/static_metadata.c
@@ -45,21 +45,21 @@
 
 grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
 uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = {
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 4, 8, 6, 2, 4, 8, 6, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 4, 8, 6, 2, 4, 8, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
 const uint8_t grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2] =
-    {11, 35, 10, 35, 12, 35, 12, 49, 13, 35, 14, 35, 15, 35, 16, 35, 17, 35,
-     19, 35, 20, 35, 21, 35, 24, 35, 25, 35, 26, 35, 27, 35, 28, 35, 29, 35,
-     30, 18, 30, 35, 31, 35, 32, 35, 36, 35, 37, 35, 38, 35, 39, 35, 42, 33,
-     42, 34, 42, 48, 42, 53, 42, 54, 42, 55, 42, 56, 43, 33, 43, 48, 43, 53,
-     46, 0,  46, 1,  46, 2,  50, 35, 57, 35, 58, 35, 59, 35, 60, 35, 61, 35,
-     62, 35, 63, 35, 64, 35, 65, 35, 66, 35, 67, 40, 67, 69, 67, 72, 68, 80,
-     68, 81, 70, 35, 71, 35, 73, 35, 74, 35, 75, 35, 76, 35, 77, 41, 77, 51,
-     77, 52, 78, 35, 79, 35, 82, 3,  82, 4,  82, 5,  82, 6,  82, 7,  82, 8,
-     82, 9,  83, 35, 84, 85, 86, 35, 87, 35, 88, 35, 89, 35, 90, 35};
+    {11, 33, 10, 33, 12, 33, 12, 50, 13, 33, 14, 33, 15, 33, 16, 33, 17, 33,
+     19, 33, 20, 33, 21, 33, 22, 33, 23, 33, 24, 33, 25, 33, 26, 33, 27, 33,
+     28, 18, 28, 33, 29, 33, 30, 33, 34, 33, 35, 33, 36, 33, 37, 33, 40, 31,
+     40, 32, 40, 49, 40, 54, 40, 55, 40, 56, 40, 57, 41, 31, 41, 49, 41, 54,
+     46, 0,  46, 1,  46, 2,  51, 33, 58, 33, 59, 33, 60, 33, 61, 33, 62, 33,
+     63, 33, 64, 33, 65, 33, 66, 33, 67, 33, 68, 33, 69, 38, 69, 71, 69, 74,
+     70, 82, 70, 83, 72, 33, 73, 33, 75, 33, 76, 33, 77, 33, 78, 33, 79, 39,
+     79, 52, 79, 53, 80, 33, 81, 33, 84, 3,  84, 4,  84, 5,  84, 6,  84, 7,
+     84, 8,  84, 9,  85, 33, 86, 87, 88, 33, 89, 33, 90, 33, 91, 33, 92, 33};
 
 const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
     "0",
@@ -84,8 +84,6 @@
     ":authority",
     "authorization",
     "cache-control",
-    "census-bin",
-    "census-binary-bin",
     "content-disposition",
     "content-encoding",
     "content-language",
@@ -108,8 +106,11 @@
     "grpc-encoding",
     "grpc-internal-encoding-request",
     "grpc-message",
+    "grpc-payload-bin",
+    "grpc-stats-bin",
     "grpc-status",
     "grpc-timeout",
+    "grpc-tracing-bin",
     "gzip",
     "gzip, deflate",
     "host",
@@ -126,7 +127,8 @@
     "if-unmodified-since",
     "last-modified",
     "link",
-    "load-reporting",
+    "load-reporting-initial",
+    "load-reporting-trailing",
     "location",
     "max-forwards",
     ":method",
diff --git a/src/core/lib/transport/static_metadata.h b/src/core/lib/transport/static_metadata.h
index 5ff0d2f..5b9ee1a 100644
--- a/src/core/lib/transport/static_metadata.h
+++ b/src/core/lib/transport/static_metadata.h
@@ -44,7 +44,7 @@
 
 #include "src/core/lib/transport/metadata.h"
 
-#define GRPC_STATIC_MDSTR_COUNT 91
+#define GRPC_STATIC_MDSTR_COUNT 93
 extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
 /* "0" */
 #define GRPC_MDSTR_0 (&grpc_static_mdstr_table[0])
@@ -90,147 +90,151 @@
 #define GRPC_MDSTR_AUTHORIZATION (&grpc_static_mdstr_table[20])
 /* "cache-control" */
 #define GRPC_MDSTR_CACHE_CONTROL (&grpc_static_mdstr_table[21])
-/* "census-bin" */
-#define GRPC_MDSTR_CENSUS_BIN (&grpc_static_mdstr_table[22])
-/* "census-binary-bin" */
-#define GRPC_MDSTR_CENSUS_BINARY_BIN (&grpc_static_mdstr_table[23])
 /* "content-disposition" */
-#define GRPC_MDSTR_CONTENT_DISPOSITION (&grpc_static_mdstr_table[24])
+#define GRPC_MDSTR_CONTENT_DISPOSITION (&grpc_static_mdstr_table[22])
 /* "content-encoding" */
-#define GRPC_MDSTR_CONTENT_ENCODING (&grpc_static_mdstr_table[25])
+#define GRPC_MDSTR_CONTENT_ENCODING (&grpc_static_mdstr_table[23])
 /* "content-language" */
-#define GRPC_MDSTR_CONTENT_LANGUAGE (&grpc_static_mdstr_table[26])
+#define GRPC_MDSTR_CONTENT_LANGUAGE (&grpc_static_mdstr_table[24])
 /* "content-length" */
-#define GRPC_MDSTR_CONTENT_LENGTH (&grpc_static_mdstr_table[27])
+#define GRPC_MDSTR_CONTENT_LENGTH (&grpc_static_mdstr_table[25])
 /* "content-location" */
-#define GRPC_MDSTR_CONTENT_LOCATION (&grpc_static_mdstr_table[28])
+#define GRPC_MDSTR_CONTENT_LOCATION (&grpc_static_mdstr_table[26])
 /* "content-range" */
-#define GRPC_MDSTR_CONTENT_RANGE (&grpc_static_mdstr_table[29])
+#define GRPC_MDSTR_CONTENT_RANGE (&grpc_static_mdstr_table[27])
 /* "content-type" */
-#define GRPC_MDSTR_CONTENT_TYPE (&grpc_static_mdstr_table[30])
+#define GRPC_MDSTR_CONTENT_TYPE (&grpc_static_mdstr_table[28])
 /* "cookie" */
-#define GRPC_MDSTR_COOKIE (&grpc_static_mdstr_table[31])
+#define GRPC_MDSTR_COOKIE (&grpc_static_mdstr_table[29])
 /* "date" */
-#define GRPC_MDSTR_DATE (&grpc_static_mdstr_table[32])
+#define GRPC_MDSTR_DATE (&grpc_static_mdstr_table[30])
 /* "deflate" */
-#define GRPC_MDSTR_DEFLATE (&grpc_static_mdstr_table[33])
+#define GRPC_MDSTR_DEFLATE (&grpc_static_mdstr_table[31])
 /* "deflate,gzip" */
-#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (&grpc_static_mdstr_table[34])
+#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (&grpc_static_mdstr_table[32])
 /* "" */
-#define GRPC_MDSTR_EMPTY (&grpc_static_mdstr_table[35])
+#define GRPC_MDSTR_EMPTY (&grpc_static_mdstr_table[33])
 /* "etag" */
-#define GRPC_MDSTR_ETAG (&grpc_static_mdstr_table[36])
+#define GRPC_MDSTR_ETAG (&grpc_static_mdstr_table[34])
 /* "expect" */
-#define GRPC_MDSTR_EXPECT (&grpc_static_mdstr_table[37])
+#define GRPC_MDSTR_EXPECT (&grpc_static_mdstr_table[35])
 /* "expires" */
-#define GRPC_MDSTR_EXPIRES (&grpc_static_mdstr_table[38])
+#define GRPC_MDSTR_EXPIRES (&grpc_static_mdstr_table[36])
 /* "from" */
-#define GRPC_MDSTR_FROM (&grpc_static_mdstr_table[39])
+#define GRPC_MDSTR_FROM (&grpc_static_mdstr_table[37])
 /* "GET" */
-#define GRPC_MDSTR_GET (&grpc_static_mdstr_table[40])
+#define GRPC_MDSTR_GET (&grpc_static_mdstr_table[38])
 /* "grpc" */
-#define GRPC_MDSTR_GRPC (&grpc_static_mdstr_table[41])
+#define GRPC_MDSTR_GRPC (&grpc_static_mdstr_table[39])
 /* "grpc-accept-encoding" */
-#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (&grpc_static_mdstr_table[42])
+#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (&grpc_static_mdstr_table[40])
 /* "grpc-encoding" */
-#define GRPC_MDSTR_GRPC_ENCODING (&grpc_static_mdstr_table[43])
+#define GRPC_MDSTR_GRPC_ENCODING (&grpc_static_mdstr_table[41])
 /* "grpc-internal-encoding-request" */
-#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (&grpc_static_mdstr_table[44])
+#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (&grpc_static_mdstr_table[42])
 /* "grpc-message" */
-#define GRPC_MDSTR_GRPC_MESSAGE (&grpc_static_mdstr_table[45])
+#define GRPC_MDSTR_GRPC_MESSAGE (&grpc_static_mdstr_table[43])
+/* "grpc-payload-bin" */
+#define GRPC_MDSTR_GRPC_PAYLOAD_BIN (&grpc_static_mdstr_table[44])
+/* "grpc-stats-bin" */
+#define GRPC_MDSTR_GRPC_STATS_BIN (&grpc_static_mdstr_table[45])
 /* "grpc-status" */
 #define GRPC_MDSTR_GRPC_STATUS (&grpc_static_mdstr_table[46])
 /* "grpc-timeout" */
 #define GRPC_MDSTR_GRPC_TIMEOUT (&grpc_static_mdstr_table[47])
+/* "grpc-tracing-bin" */
+#define GRPC_MDSTR_GRPC_TRACING_BIN (&grpc_static_mdstr_table[48])
 /* "gzip" */
-#define GRPC_MDSTR_GZIP (&grpc_static_mdstr_table[48])
+#define GRPC_MDSTR_GZIP (&grpc_static_mdstr_table[49])
 /* "gzip, deflate" */
-#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (&grpc_static_mdstr_table[49])
+#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (&grpc_static_mdstr_table[50])
 /* "host" */
-#define GRPC_MDSTR_HOST (&grpc_static_mdstr_table[50])
+#define GRPC_MDSTR_HOST (&grpc_static_mdstr_table[51])
 /* "http" */
-#define GRPC_MDSTR_HTTP (&grpc_static_mdstr_table[51])
+#define GRPC_MDSTR_HTTP (&grpc_static_mdstr_table[52])
 /* "https" */
-#define GRPC_MDSTR_HTTPS (&grpc_static_mdstr_table[52])
+#define GRPC_MDSTR_HTTPS (&grpc_static_mdstr_table[53])
 /* "identity" */
-#define GRPC_MDSTR_IDENTITY (&grpc_static_mdstr_table[53])
+#define GRPC_MDSTR_IDENTITY (&grpc_static_mdstr_table[54])
 /* "identity,deflate" */
-#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (&grpc_static_mdstr_table[54])
+#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (&grpc_static_mdstr_table[55])
 /* "identity,deflate,gzip" */
 #define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \
-  (&grpc_static_mdstr_table[55])
+  (&grpc_static_mdstr_table[56])
 /* "identity,gzip" */
-#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (&grpc_static_mdstr_table[56])
+#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (&grpc_static_mdstr_table[57])
 /* "if-match" */
-#define GRPC_MDSTR_IF_MATCH (&grpc_static_mdstr_table[57])
+#define GRPC_MDSTR_IF_MATCH (&grpc_static_mdstr_table[58])
 /* "if-modified-since" */
-#define GRPC_MDSTR_IF_MODIFIED_SINCE (&grpc_static_mdstr_table[58])
+#define GRPC_MDSTR_IF_MODIFIED_SINCE (&grpc_static_mdstr_table[59])
 /* "if-none-match" */
-#define GRPC_MDSTR_IF_NONE_MATCH (&grpc_static_mdstr_table[59])
+#define GRPC_MDSTR_IF_NONE_MATCH (&grpc_static_mdstr_table[60])
 /* "if-range" */
-#define GRPC_MDSTR_IF_RANGE (&grpc_static_mdstr_table[60])
+#define GRPC_MDSTR_IF_RANGE (&grpc_static_mdstr_table[61])
 /* "if-unmodified-since" */
-#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[61])
+#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[62])
 /* "last-modified" */
-#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[62])
+#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[63])
 /* "link" */
-#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[63])
-/* "load-reporting" */
-#define GRPC_MDSTR_LOAD_REPORTING (&grpc_static_mdstr_table[64])
+#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[64])
+/* "load-reporting-initial" */
+#define GRPC_MDSTR_LOAD_REPORTING_INITIAL (&grpc_static_mdstr_table[65])
+/* "load-reporting-trailing" */
+#define GRPC_MDSTR_LOAD_REPORTING_TRAILING (&grpc_static_mdstr_table[66])
 /* "location" */
-#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[65])
+#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[67])
 /* "max-forwards" */
-#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[66])
+#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[68])
 /* ":method" */
-#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[67])
+#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[69])
 /* ":path" */
-#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[68])
+#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[70])
 /* "POST" */
-#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[69])
+#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[71])
 /* "proxy-authenticate" */
-#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[70])
+#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[72])
 /* "proxy-authorization" */
-#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[71])
+#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[73])
 /* "PUT" */
-#define GRPC_MDSTR_PUT (&grpc_static_mdstr_table[72])
+#define GRPC_MDSTR_PUT (&grpc_static_mdstr_table[74])
 /* "range" */
-#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[73])
+#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[75])
 /* "referer" */
-#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[74])
+#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[76])
 /* "refresh" */
-#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[75])
+#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[77])
 /* "retry-after" */
-#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[76])
+#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[78])
 /* ":scheme" */
-#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[77])
+#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[79])
 /* "server" */
-#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[78])
+#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[80])
 /* "set-cookie" */
-#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[79])
+#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[81])
 /* "/" */
-#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[80])
+#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[82])
 /* "/index.html" */
-#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[81])
+#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[83])
 /* ":status" */
-#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[82])
+#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[84])
 /* "strict-transport-security" */
-#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[83])
+#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[85])
 /* "te" */
-#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[84])
+#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[86])
 /* "trailers" */
-#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[85])
+#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[87])
 /* "transfer-encoding" */
-#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[86])
+#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[88])
 /* "user-agent" */
-#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[87])
+#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[89])
 /* "vary" */
-#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[88])
+#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[90])
 /* "via" */
-#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[89])
+#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[91])
 /* "www-authenticate" */
-#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[90])
+#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[92])
 
-#define GRPC_STATIC_MDELEM_COUNT 80
+#define GRPC_STATIC_MDELEM_COUNT 81
 extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
 extern uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];
 /* "accept-charset": "" */
@@ -335,73 +339,76 @@
 #define GRPC_MDELEM_LAST_MODIFIED_EMPTY (&grpc_static_mdelem_table[45])
 /* "link": "" */
 #define GRPC_MDELEM_LINK_EMPTY (&grpc_static_mdelem_table[46])
-/* "load-reporting": "" */
-#define GRPC_MDELEM_LOAD_REPORTING_EMPTY (&grpc_static_mdelem_table[47])
+/* "load-reporting-initial": "" */
+#define GRPC_MDELEM_LOAD_REPORTING_INITIAL_EMPTY (&grpc_static_mdelem_table[47])
+/* "load-reporting-trailing": "" */
+#define GRPC_MDELEM_LOAD_REPORTING_TRAILING_EMPTY \
+  (&grpc_static_mdelem_table[48])
 /* "location": "" */
-#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[48])
+#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[49])
 /* "max-forwards": "" */
-#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[49])
+#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[50])
 /* ":method": "GET" */
-#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[50])
+#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[51])
 /* ":method": "POST" */
-#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[51])
+#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[52])
 /* ":method": "PUT" */
-#define GRPC_MDELEM_METHOD_PUT (&grpc_static_mdelem_table[52])
+#define GRPC_MDELEM_METHOD_PUT (&grpc_static_mdelem_table[53])
 /* ":path": "/" */
-#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[53])
+#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[54])
 /* ":path": "/index.html" */
-#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[54])
+#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[55])
 /* "proxy-authenticate": "" */
-#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[55])
+#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[56])
 /* "proxy-authorization": "" */
-#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[56])
+#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[57])
 /* "range": "" */
-#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[57])
+#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[58])
 /* "referer": "" */
-#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[58])
+#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[59])
 /* "refresh": "" */
-#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[59])
+#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[60])
 /* "retry-after": "" */
-#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[60])
+#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[61])
 /* ":scheme": "grpc" */
-#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[61])
+#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[62])
 /* ":scheme": "http" */
-#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[62])
+#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[63])
 /* ":scheme": "https" */
-#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[63])
+#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[64])
 /* "server": "" */
-#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[64])
+#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[65])
 /* "set-cookie": "" */
-#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[65])
+#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[66])
 /* ":status": "200" */
-#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[66])
+#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[67])
 /* ":status": "204" */
-#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[67])
+#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[68])
 /* ":status": "206" */
-#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[68])
+#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[69])
 /* ":status": "304" */
-#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[69])
+#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[70])
 /* ":status": "400" */
-#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[70])
+#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[71])
 /* ":status": "404" */
-#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[71])
+#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[72])
 /* ":status": "500" */
-#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[72])
+#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[73])
 /* "strict-transport-security": "" */
 #define GRPC_MDELEM_STRICT_TRANSPORT_SECURITY_EMPTY \
-  (&grpc_static_mdelem_table[73])
+  (&grpc_static_mdelem_table[74])
 /* "te": "trailers" */
-#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[74])
+#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[75])
 /* "transfer-encoding": "" */
-#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[75])
+#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[76])
 /* "user-agent": "" */
-#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[76])
+#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[77])
 /* "vary": "" */
-#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[77])
+#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[78])
 /* "via": "" */
-#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[78])
+#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[79])
 /* "www-authenticate": "" */
-#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[79])
+#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[80])
 
 extern const uint8_t
     grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2];
diff --git a/src/core/lib/transport/timeout_encoding.c b/src/core/lib/transport/timeout_encoding.c
new file mode 100644
index 0000000..b58ebbd
--- /dev/null
+++ b/src/core/lib/transport/timeout_encoding.c
@@ -0,0 +1,188 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/transport/timeout_encoding.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/port_platform.h>
+#include "src/core/lib/support/string.h"
+
+static int64_t round_up(int64_t x, int64_t divisor) {
+  return (x / divisor + (x % divisor != 0)) * divisor;
+}
+
+/* round an integer up to the next value with three significant figures */
+static int64_t round_up_to_three_sig_figs(int64_t x) {
+  if (x < 1000) return x;
+  if (x < 10000) return round_up(x, 10);
+  if (x < 100000) return round_up(x, 100);
+  if (x < 1000000) return round_up(x, 1000);
+  if (x < 10000000) return round_up(x, 10000);
+  if (x < 100000000) return round_up(x, 100000);
+  if (x < 1000000000) return round_up(x, 1000000);
+  return round_up(x, 10000000);
+}
+
+/* encode our minimum viable timeout value */
+static void enc_tiny(char *buffer) { memcpy(buffer, "1n", 3); }
+
+static void enc_ext(char *buffer, int64_t value, char ext) {
+  int n = int64_ttoa(value, buffer);
+  buffer[n] = ext;
+  buffer[n + 1] = 0;
+}
+
+static void enc_seconds(char *buffer, int64_t sec) {
+  if (sec % 3600 == 0) {
+    enc_ext(buffer, sec / 3600, 'H');
+  } else if (sec % 60 == 0) {
+    enc_ext(buffer, sec / 60, 'M');
+  } else {
+    enc_ext(buffer, sec, 'S');
+  }
+}
+
+static void enc_nanos(char *buffer, int64_t x) {
+  x = round_up_to_three_sig_figs(x);
+  if (x < 100000) {
+    if (x % 1000 == 0) {
+      enc_ext(buffer, x / 1000, 'u');
+    } else {
+      enc_ext(buffer, x, 'n');
+    }
+  } else if (x < 100000000) {
+    if (x % 1000000 == 0) {
+      enc_ext(buffer, x / 1000000, 'm');
+    } else {
+      enc_ext(buffer, x / 1000, 'u');
+    }
+  } else if (x < 1000000000) {
+    enc_ext(buffer, x / 1000000, 'm');
+  } else {
+    /* note that this is only ever called with times of less than one second,
+       so if we reach here the time must have been rounded up to a whole second
+       (and no more) */
+    memcpy(buffer, "1S", 3);
+  }
+}
+
+static void enc_micros(char *buffer, int64_t x) {
+  x = round_up_to_three_sig_figs(x);
+  if (x < 100000) {
+    if (x % 1000 == 0) {
+      enc_ext(buffer, x / 1000, 'm');
+    } else {
+      enc_ext(buffer, x, 'u');
+    }
+  } else if (x < 100000000) {
+    if (x % 1000000 == 0) {
+      enc_ext(buffer, x / 1000000, 'S');
+    } else {
+      enc_ext(buffer, x / 1000, 'm');
+    }
+  } else {
+    enc_ext(buffer, x / 1000000, 'S');
+  }
+}
+
+void grpc_http2_encode_timeout(gpr_timespec timeout, char *buffer) {
+  if (timeout.tv_sec < 0) {
+    enc_tiny(buffer);
+  } else if (timeout.tv_sec == 0) {
+    enc_nanos(buffer, timeout.tv_nsec);
+  } else if (timeout.tv_sec < 1000 && timeout.tv_nsec != 0) {
+    enc_micros(buffer,
+               (int64_t)(timeout.tv_sec * 1000000) +
+                   (timeout.tv_nsec / 1000 + (timeout.tv_nsec % 1000 != 0)));
+  } else {
+    enc_seconds(buffer, timeout.tv_sec + (timeout.tv_nsec != 0));
+  }
+}
+
+static int is_all_whitespace(const char *p) {
+  while (*p == ' ') p++;
+  return *p == 0;
+}
+
+int grpc_http2_decode_timeout(const char *buffer, gpr_timespec *timeout) {
+  int32_t x = 0;
+  const uint8_t *p = (const uint8_t *)buffer;
+  int have_digit = 0;
+  /* skip whitespace */
+  for (; *p == ' '; p++)
+    ;
+  /* decode numeric part */
+  for (; *p >= '0' && *p <= '9'; p++) {
+    int32_t digit = (int32_t)(*p - (uint8_t)'0');
+    have_digit = 1;
+    /* spec allows max. 8 digits, but we allow values up to 1,000,000,000 */
+    if (x >= (100 * 1000 * 1000)) {
+      if (x != (100 * 1000 * 1000) || digit != 0) {
+        *timeout = gpr_inf_future(GPR_TIMESPAN);
+        return 1;
+      }
+    }
+    x = x * 10 + digit;
+  }
+  if (!have_digit) return 0;
+  /* skip whitespace */
+  for (; *p == ' '; p++)
+    ;
+  /* decode unit specifier */
+  switch (*p) {
+    case 'n':
+      *timeout = gpr_time_from_nanos(x, GPR_TIMESPAN);
+      break;
+    case 'u':
+      *timeout = gpr_time_from_micros(x, GPR_TIMESPAN);
+      break;
+    case 'm':
+      *timeout = gpr_time_from_millis(x, GPR_TIMESPAN);
+      break;
+    case 'S':
+      *timeout = gpr_time_from_seconds(x, GPR_TIMESPAN);
+      break;
+    case 'M':
+      *timeout = gpr_time_from_minutes(x, GPR_TIMESPAN);
+      break;
+    case 'H':
+      *timeout = gpr_time_from_hours(x, GPR_TIMESPAN);
+      break;
+    default:
+      return 0;
+  }
+  p++;
+  return is_all_whitespace((const char *)p);
+}
diff --git a/src/core/lib/transport/timeout_encoding.h b/src/core/lib/transport/timeout_encoding.h
new file mode 100644
index 0000000..92f02f6
--- /dev/null
+++ b/src/core/lib/transport/timeout_encoding.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H
+#define GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H
+
+#include <grpc/support/time.h>
+#include "src/core/lib/support/string.h"
+
+#define GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE (GPR_LTOA_MIN_BUFSIZE + 1)
+
+/* Encode/decode timeouts to the GRPC over HTTP/2 format;
+   encoding may round up arbitrarily */
+void grpc_http2_encode_timeout(gpr_timespec timeout, char *buffer);
+int grpc_http2_decode_timeout(const char *buffer, gpr_timespec *timeout);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H */
diff --git a/src/core/lib/transport/transport.c b/src/core/lib/transport/transport.c
index 857c390..82fc605 100644
--- a/src/core/lib/transport/transport.c
+++ b/src/core/lib/transport/transport.c
@@ -32,10 +32,14 @@
  */
 
 #include "src/core/lib/transport/transport.h"
+
+#include <string.h>
+
 #include <grpc/support/alloc.h>
 #include <grpc/support/atm.h>
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
+
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/transport_impl.h"
 
@@ -43,7 +47,7 @@
 void grpc_stream_ref(grpc_stream_refcount *refcount, const char *reason) {
   gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count);
   gpr_log(GPR_DEBUG, "%s %p:%p   REF %d->%d %s", refcount->object_type,
-          refcount, refcount->destroy.cb_arg, val, val + 1, reason);
+          refcount, refcount->destroy.cb_arg, (int)val, (int)val + 1, reason);
 #else
 void grpc_stream_ref(grpc_stream_refcount *refcount) {
 #endif
@@ -55,7 +59,7 @@
                        const char *reason) {
   gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count);
   gpr_log(GPR_DEBUG, "%s %p:%p UNREF %d->%d %s", refcount->object_type,
-          refcount, refcount->destroy.cb_arg, val, val - 1, reason);
+          refcount, refcount->destroy.cb_arg, (int)val, (int)val - 1, reason);
 #else
 void grpc_stream_unref(grpc_exec_ctx *exec_ctx,
                        grpc_stream_refcount *refcount) {
@@ -220,7 +224,7 @@
     error = GRPC_ERROR_CREATE("Call cancelled");
   }
   error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, status);
-  add_error(op, &op->close_error, error);
+  add_error(op, &op->cancel_error, error);
 }
 
 void grpc_transport_stream_op_add_close(grpc_transport_stream_op *op,
@@ -247,3 +251,26 @@
   error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, status);
   add_error(op, &op->close_error, error);
 }
+
+typedef struct {
+  grpc_closure outer_on_complete;
+  grpc_closure *inner_on_complete;
+  grpc_transport_op op;
+} made_transport_op;
+
+static void destroy_made_transport_op(grpc_exec_ctx *exec_ctx, void *arg,
+                                      grpc_error *error) {
+  made_transport_op *op = arg;
+  grpc_exec_ctx_sched(exec_ctx, op->inner_on_complete, GRPC_ERROR_REF(error),
+                      NULL);
+  gpr_free(op);
+}
+
+grpc_transport_op *grpc_make_transport_op(grpc_closure *on_complete) {
+  made_transport_op *op = gpr_malloc(sizeof(*op));
+  grpc_closure_init(&op->outer_on_complete, destroy_made_transport_op, op);
+  op->inner_on_complete = on_complete;
+  memset(&op->op, 0, sizeof(op->op));
+  op->op.on_consumed = &op->outer_on_complete;
+  return &op->op;
+}
diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
index e33fc5c..8dc393f 100644
--- a/src/core/lib/transport/transport.h
+++ b/src/core/lib/transport/transport.h
@@ -43,6 +43,10 @@
 #include "src/core/lib/transport/byte_stream.h"
 #include "src/core/lib/transport/metadata_batch.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* forward declarations */
 typedef struct grpc_transport grpc_transport;
 
@@ -96,6 +100,11 @@
 void grpc_transport_move_stats(grpc_transport_stream_stats *from,
                                grpc_transport_stream_stats *to);
 
+typedef struct {
+  grpc_closure closure;
+  void *args[2];
+} grpc_transport_private_op_data;
+
 /* Transport stream op: a set of operations to perform on a transport
    against a single stream */
 typedef struct grpc_transport_stream_op {
@@ -120,6 +129,7 @@
   /** Receive initial metadata from the stream, into provided metadata batch. */
   grpc_metadata_batch *recv_initial_metadata;
   bool *recv_idempotent_request;
+  bool *recv_cacheable_request;
   /** Should be enqueued when initial metadata is ready to be processed. */
   grpc_closure *recv_initial_metadata_ready;
 
@@ -144,6 +154,12 @@
 
   /* Indexes correspond to grpc_context_index enum values */
   grpc_call_context_element *context;
+
+  /***************************************************************************
+   * remaining fields are initialized and used at the discretion of the
+   * transport implementation */
+
+  grpc_transport_private_op_data transport_private;
 } grpc_transport_stream_op;
 
 /** Transport op: a set of operations to perform on a transport as a whole */
@@ -158,7 +174,7 @@
   /** should we send a goaway?
       after a goaway is sent, once there are no more active calls on
       the transport, the transport should disconnect */
-  int send_goaway;
+  bool send_goaway;
   /** what should the goaway contain? */
   grpc_status_code goaway_status;
   gpr_slice *goaway_message;
@@ -177,6 +193,12 @@
   grpc_pollset_set *bind_pollset_set;
   /** send a ping, call this back if not NULL */
   grpc_closure *send_ping;
+
+  /***************************************************************************
+   * remaining fields are initialized and used at the discretion of the
+   * transport implementation */
+
+  grpc_transport_private_op_data transport_private;
 } grpc_transport_op;
 
 /* Returns the amount of memory required to store a grpc_stream for this
@@ -268,4 +290,12 @@
 char *grpc_transport_get_peer(grpc_exec_ctx *exec_ctx,
                               grpc_transport *transport);
 
+/* Allocate a grpc_transport_op, and preconfigure the on_consumed closure to
+   \a on_consumed and then delete the returned transport op */
+grpc_transport_op *grpc_make_transport_op(grpc_closure *on_consumed);
+
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* GRPC_CORE_LIB_TRANSPORT_TRANSPORT_H */
diff --git a/src/core/plugin_registry/grpc_plugin_registry.c b/src/core/plugin_registry/grpc_plugin_registry.c
index 905cd59..7a7a9ce 100644
--- a/src/core/plugin_registry/grpc_plugin_registry.c
+++ b/src/core/plugin_registry/grpc_plugin_registry.c
@@ -37,6 +37,8 @@
 extern void grpc_chttp2_plugin_shutdown(void);
 extern void grpc_client_config_init(void);
 extern void grpc_client_config_shutdown(void);
+extern void grpc_lb_policy_grpclb_init(void);
+extern void grpc_lb_policy_grpclb_shutdown(void);
 extern void grpc_lb_policy_pick_first_init(void);
 extern void grpc_lb_policy_pick_first_shutdown(void);
 extern void grpc_lb_policy_round_robin_init(void);
@@ -55,6 +57,8 @@
                        grpc_chttp2_plugin_shutdown);
   grpc_register_plugin(grpc_client_config_init,
                        grpc_client_config_shutdown);
+  grpc_register_plugin(grpc_lb_policy_grpclb_init,
+                       grpc_lb_policy_grpclb_shutdown);
   grpc_register_plugin(grpc_lb_policy_pick_first_init,
                        grpc_lb_policy_pick_first_shutdown);
   grpc_register_plugin(grpc_lb_policy_round_robin_init,
diff --git a/src/core/plugin_registry/grpc_unsecure_plugin_registry.c b/src/core/plugin_registry/grpc_unsecure_plugin_registry.c
index 7995078..ad4ddf0 100644
--- a/src/core/plugin_registry/grpc_unsecure_plugin_registry.c
+++ b/src/core/plugin_registry/grpc_unsecure_plugin_registry.c
@@ -43,6 +43,8 @@
 extern void grpc_resolver_sockaddr_shutdown(void);
 extern void grpc_load_reporting_plugin_init(void);
 extern void grpc_load_reporting_plugin_shutdown(void);
+extern void grpc_lb_policy_grpclb_init(void);
+extern void grpc_lb_policy_grpclb_shutdown(void);
 extern void grpc_lb_policy_pick_first_init(void);
 extern void grpc_lb_policy_pick_first_shutdown(void);
 extern void grpc_lb_policy_round_robin_init(void);
@@ -61,6 +63,8 @@
                        grpc_resolver_sockaddr_shutdown);
   grpc_register_plugin(grpc_load_reporting_plugin_init,
                        grpc_load_reporting_plugin_shutdown);
+  grpc_register_plugin(grpc_lb_policy_grpclb_init,
+                       grpc_lb_policy_grpclb_shutdown);
   grpc_register_plugin(grpc_lb_policy_pick_first_init,
                        grpc_lb_policy_pick_first_shutdown);
   grpc_register_plugin(grpc_lb_policy_round_robin_init,
diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel_cc.cc
similarity index 100%
rename from src/cpp/client/channel.cc
rename to src/cpp/client/channel_cc.cc
diff --git a/src/cpp/client/client_context.cc b/src/cpp/client/client_context.cc
index 0ba77a5..5b6aaa7 100644
--- a/src/cpp/client/client_context.cc
+++ b/src/cpp/client/client_context.cc
@@ -36,6 +36,7 @@
 #include <grpc/compression.h>
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
 #include <grpc++/security/credentials.h>
@@ -60,6 +61,7 @@
     : initial_metadata_received_(false),
       fail_fast_(true),
       idempotent_(false),
+      cacheable_(false),
       call_(nullptr),
       call_canceled_(false),
       deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)),
diff --git a/src/cpp/client/create_channel_posix.cc b/src/cpp/client/create_channel_posix.cc
index 60cfed3..eb323ac 100644
--- a/src/cpp/client/create_channel_posix.cc
+++ b/src/cpp/client/create_channel_posix.cc
@@ -51,6 +51,16 @@
       "", grpc_insecure_channel_create_from_fd(target.c_str(), fd, nullptr));
 }
 
+std::shared_ptr<Channel> CreateCustomInsecureChannelFromFd(
+    const grpc::string& target, int fd, const ChannelArguments& args) {
+  internal::GrpcLibrary init_lib;
+  init_lib.init();
+  grpc_channel_args channel_args;
+  args.SetChannelArgs(&channel_args);
+  return CreateChannelInternal("", grpc_insecure_channel_create_from_fd(
+                                       target.c_str(), fd, &channel_args));
+}
+
 #endif  // GPR_SUPPORT_CHANNELS_FROM_FD
 
 }  // namespace grpc
diff --git a/src/cpp/client/credentials.cc b/src/cpp/client/credentials_cc.cc
similarity index 100%
rename from src/cpp/client/credentials.cc
rename to src/cpp/client/credentials_cc.cc
diff --git a/src/cpp/common/channel_filter.cc b/src/cpp/common/channel_filter.cc
new file mode 100644
index 0000000..25cd49c
--- /dev/null
+++ b/src/cpp/common/channel_filter.cc
@@ -0,0 +1,112 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <string.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/cpp/common/channel_filter.h"
+
+namespace grpc {
+
+// MetadataBatch
+
+grpc_linked_mdelem *MetadataBatch::AddMetadata(const string &key,
+                                               const string &value) {
+  grpc_linked_mdelem *storage = new grpc_linked_mdelem;
+  memset(storage, 0, sizeof(grpc_linked_mdelem));
+  storage->md = grpc_mdelem_from_strings(key.c_str(), value.c_str());
+  grpc_metadata_batch_link_head(batch_, storage);
+  return storage;
+}
+
+// ChannelData
+
+void ChannelData::StartTransportOp(grpc_exec_ctx *exec_ctx,
+                                   grpc_channel_element *elem,
+                                   TransportOp *op) {
+  grpc_channel_next_op(exec_ctx, elem, op->op());
+}
+
+// CallData
+
+void CallData::StartTransportStreamOp(grpc_exec_ctx *exec_ctx,
+                                      grpc_call_element *elem,
+                                      TransportStreamOp *op) {
+  grpc_call_next_op(exec_ctx, elem, op->op());
+}
+
+void CallData::SetPollsetOrPollsetSet(grpc_exec_ctx *exec_ctx,
+                                      grpc_call_element *elem,
+                                      grpc_polling_entity *pollent) {
+  grpc_call_stack_ignore_set_pollset_or_pollset_set(exec_ctx, elem, pollent);
+}
+
+char *CallData::GetPeer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {
+  return grpc_call_next_get_peer(exec_ctx, elem);
+}
+
+// internal code used by RegisterChannelFilter()
+
+namespace internal {
+
+// Note: Implicitly initialized to nullptr due to static lifetime.
+std::vector<FilterRecord> *channel_filters;
+
+namespace {
+
+bool MaybeAddFilter(grpc_channel_stack_builder *builder, void *arg) {
+  const FilterRecord &filter = *(FilterRecord *)arg;
+  if (filter.include_filter) {
+    const grpc_channel_args *args =
+        grpc_channel_stack_builder_get_channel_arguments(builder);
+    if (!filter.include_filter(*args)) return true;
+  }
+  return grpc_channel_stack_builder_prepend_filter(builder, &filter.filter,
+                                                   nullptr, nullptr);
+}
+
+}  // namespace
+
+void ChannelFilterPluginInit() {
+  for (size_t i = 0; i < channel_filters->size(); ++i) {
+    FilterRecord &filter = (*channel_filters)[i];
+    grpc_channel_init_register_stage(filter.stack_type, filter.priority,
+                                     MaybeAddFilter, (void *)&filter);
+  }
+}
+
+void ChannelFilterPluginShutdown() {}
+
+}  // namespace internal
+
+}  // namespace grpc
diff --git a/src/cpp/common/channel_filter.h b/src/cpp/common/channel_filter.h
new file mode 100644
index 0000000..6f5af3d
--- /dev/null
+++ b/src/cpp/common/channel_filter.h
@@ -0,0 +1,389 @@
+/*
+ *
+ * Copyright 2016, 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 GRPCXX_CHANNEL_FILTER_H
+#define GRPCXX_CHANNEL_FILTER_H
+
+#include <grpc++/impl/codegen/config.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+
+#include <functional>
+#include <vector>
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/transport/metadata_batch.h"
+
+/// An interface to define filters.
+///
+/// To define a filter, implement a subclass of each of \c CallData and
+/// \c ChannelData. Then register the filter using something like this:
+/// \code{.cpp}
+///   RegisterChannelFilter<MyChannelDataSubclass, MyCallDataSubclass>(
+///       "name-of-filter", GRPC_SERVER_CHANNEL, INT_MAX, nullptr);
+/// \endcode
+
+/// Forward declaration to avoid including the file
+/// "src/core/lib/security/context/security_context.h"
+struct grpc_client_security_context;
+struct grpc_server_security_context;
+
+namespace grpc {
+
+/// A C++ wrapper for the \c grpc_metadata_batch struct.
+class MetadataBatch {
+ public:
+  /// Borrows a pointer to \a batch, but does NOT take ownership.
+  /// The caller must ensure that \a batch continues to exist for as
+  /// long as the MetadataBatch object does.
+  explicit MetadataBatch(grpc_metadata_batch *batch) : batch_(batch) {}
+
+  grpc_metadata_batch *batch() const { return batch_; }
+
+  /// Adds metadata and returns the newly allocated storage.
+  /// The caller takes ownership of the result, which must exist for the
+  /// lifetime of the gRPC call.
+  grpc_linked_mdelem *AddMetadata(const string &key, const string &value);
+
+  class const_iterator : public std::iterator<std::bidirectional_iterator_tag,
+                                              const grpc_mdelem> {
+   public:
+    const grpc_mdelem &operator*() const { return *elem_->md; }
+    const grpc_mdelem *operator->() const { return elem_->md; }
+
+    const_iterator &operator++() {
+      elem_ = elem_->next;
+      return *this;
+    }
+    const_iterator operator++(int) {
+      const_iterator tmp(*this);
+      operator++();
+      return tmp;
+    }
+    const_iterator &operator--() {
+      elem_ = elem_->prev;
+      return *this;
+    }
+    const_iterator operator--(int) {
+      const_iterator tmp(*this);
+      operator--();
+      return tmp;
+    }
+
+    bool operator==(const const_iterator &other) const {
+      return elem_ == other.elem_;
+    }
+    bool operator!=(const const_iterator &other) const {
+      return elem_ != other.elem_;
+    }
+
+   private:
+    friend class MetadataBatch;
+    explicit const_iterator(grpc_linked_mdelem *elem) : elem_(elem) {}
+
+    grpc_linked_mdelem *elem_;
+  };
+
+  const_iterator begin() const { return const_iterator(batch_->list.head); }
+  const_iterator end() const { return const_iterator(nullptr); }
+
+ private:
+  grpc_metadata_batch *batch_;  // Not owned.
+};
+
+/// A C++ wrapper for the \c grpc_transport_op struct.
+class TransportOp {
+ public:
+  /// Borrows a pointer to \a op, but does NOT take ownership.
+  /// The caller must ensure that \a op continues to exist for as
+  /// long as the TransportOp object does.
+  explicit TransportOp(grpc_transport_op *op) : op_(op) {}
+
+  grpc_transport_op *op() const { return op_; }
+
+  // TODO(roth): Add a C++ wrapper for grpc_error?
+  grpc_error *disconnect_with_error() const {
+    return op_->disconnect_with_error;
+  }
+  bool send_goaway() const { return op_->send_goaway; }
+
+  // TODO(roth): Add methods for additional fields as needed.
+
+ private:
+  grpc_transport_op *op_;  // Not owned.
+};
+
+/// A C++ wrapper for the \c grpc_transport_stream_op struct.
+class TransportStreamOp {
+ public:
+  /// Borrows a pointer to \a op, but does NOT take ownership.
+  /// The caller must ensure that \a op continues to exist for as
+  /// long as the TransportStreamOp object does.
+  explicit TransportStreamOp(grpc_transport_stream_op *op)
+      : op_(op),
+        send_initial_metadata_(op->send_initial_metadata),
+        send_trailing_metadata_(op->send_trailing_metadata),
+        recv_initial_metadata_(op->recv_initial_metadata),
+        recv_trailing_metadata_(op->recv_trailing_metadata) {}
+
+  grpc_transport_stream_op *op() const { return op_; }
+
+  grpc_closure *on_complete() const { return op_->on_complete; }
+  void set_on_complete(grpc_closure *closure) { op_->on_complete = closure; }
+
+  MetadataBatch *send_initial_metadata() {
+    return op_->send_initial_metadata == nullptr ? nullptr
+                                                 : &send_initial_metadata_;
+  }
+  MetadataBatch *send_trailing_metadata() {
+    return op_->send_trailing_metadata == nullptr ? nullptr
+                                                  : &send_trailing_metadata_;
+  }
+  MetadataBatch *recv_initial_metadata() {
+    return op_->recv_initial_metadata == nullptr ? nullptr
+                                                 : &recv_initial_metadata_;
+  }
+  MetadataBatch *recv_trailing_metadata() {
+    return op_->recv_trailing_metadata == nullptr ? nullptr
+                                                  : &recv_trailing_metadata_;
+  }
+
+  uint32_t *send_initial_metadata_flags() const {
+    return &op_->send_initial_metadata_flags;
+  }
+
+  grpc_closure *recv_initial_metadata_ready() const {
+    return op_->recv_initial_metadata_ready;
+  }
+  void set_recv_initial_metadata_ready(grpc_closure *closure) {
+    op_->recv_initial_metadata_ready = closure;
+  }
+
+  grpc_byte_stream *send_message() const { return op_->send_message; }
+  void set_send_message(grpc_byte_stream *send_message) {
+    op_->send_message = send_message;
+  }
+
+  /// To be called only on clients and servers, respectively.
+  grpc_client_security_context *client_security_context() const {
+    return (grpc_client_security_context *)op_->context[GRPC_CONTEXT_SECURITY]
+        .value;
+  }
+  grpc_server_security_context *server_security_context() const {
+    return (grpc_server_security_context *)op_->context[GRPC_CONTEXT_SECURITY]
+        .value;
+  }
+
+  census_context *get_census_context() const {
+    return (census_context *)op_->context[GRPC_CONTEXT_TRACING].value;
+  }
+
+ private:
+  grpc_transport_stream_op *op_;  // Not owned.
+  MetadataBatch send_initial_metadata_;
+  MetadataBatch send_trailing_metadata_;
+  MetadataBatch recv_initial_metadata_;
+  MetadataBatch recv_trailing_metadata_;
+};
+
+/// Represents channel data.
+class ChannelData {
+ public:
+  virtual ~ChannelData() {
+    if (peer_) gpr_free((void *)peer_);
+  }
+
+  /// Caller does NOT take ownership of result.
+  const char *peer() const { return peer_; }
+
+  // TODO(roth): Find a way to avoid passing elem into these methods.
+  virtual void StartTransportOp(grpc_exec_ctx *exec_ctx,
+                                grpc_channel_element *elem, TransportOp *op);
+
+ protected:
+  /// Takes ownership of \a peer.
+  ChannelData(const grpc_channel_args &args, const char *peer) : peer_(peer) {}
+
+ private:
+  const char *peer_;
+};
+
+/// Represents call data.
+class CallData {
+ public:
+  virtual ~CallData() {}
+
+  /// Initializes the call data.
+  virtual grpc_error *Init() { return GRPC_ERROR_NONE; }
+
+  // TODO(roth): Find a way to avoid passing elem into these methods.
+
+  /// Starts a new stream operation.
+  virtual void StartTransportStreamOp(grpc_exec_ctx *exec_ctx,
+                                      grpc_call_element *elem,
+                                      TransportStreamOp *op);
+
+  /// Sets a pollset or pollset set.
+  virtual void SetPollsetOrPollsetSet(grpc_exec_ctx *exec_ctx,
+                                      grpc_call_element *elem,
+                                      grpc_polling_entity *pollent);
+
+  /// Gets the peer name.
+  virtual char *GetPeer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem);
+
+ protected:
+  explicit CallData(const ChannelData &) {}
+};
+
+namespace internal {
+
+// Defines static members for passing to C core.
+// Members of this class correspond to the members of the C
+// grpc_channel_filter struct.
+template <typename ChannelDataType, typename CallDataType>
+class ChannelFilter GRPC_FINAL {
+ public:
+  static const size_t channel_data_size = sizeof(ChannelDataType);
+
+  static void InitChannelElement(grpc_exec_ctx *exec_ctx,
+                                 grpc_channel_element *elem,
+                                 grpc_channel_element_args *args) {
+    const char *peer =
+        args->optional_transport
+            ? grpc_transport_get_peer(exec_ctx, args->optional_transport)
+            : nullptr;
+    // Construct the object in the already-allocated memory.
+    new (elem->channel_data) ChannelDataType(*args->channel_args, peer);
+  }
+
+  static void DestroyChannelElement(grpc_exec_ctx *exec_ctx,
+                                    grpc_channel_element *elem) {
+    reinterpret_cast<ChannelDataType *>(elem->channel_data)->~ChannelDataType();
+  }
+
+  static void StartTransportOp(grpc_exec_ctx *exec_ctx,
+                               grpc_channel_element *elem,
+                               grpc_transport_op *op) {
+    ChannelDataType *channel_data = (ChannelDataType *)elem->channel_data;
+    TransportOp op_wrapper(op);
+    channel_data->StartTransportOp(exec_ctx, elem, &op_wrapper);
+  }
+
+  static const size_t call_data_size = sizeof(CallDataType);
+
+  static grpc_error *InitCallElement(grpc_exec_ctx *exec_ctx,
+                                     grpc_call_element *elem,
+                                     grpc_call_element_args *args) {
+    const ChannelDataType &channel_data =
+        *(ChannelDataType *)elem->channel_data;
+    // Construct the object in the already-allocated memory.
+    CallDataType *call_data = new (elem->call_data) CallDataType(channel_data);
+    return call_data->Init();
+  }
+
+  static void DestroyCallElement(grpc_exec_ctx *exec_ctx,
+                                 grpc_call_element *elem,
+                                 const grpc_call_final_info *final_info,
+                                 void *and_free_memory) {
+    reinterpret_cast<CallDataType *>(elem->call_data)->~CallDataType();
+  }
+
+  static void StartTransportStreamOp(grpc_exec_ctx *exec_ctx,
+                                     grpc_call_element *elem,
+                                     grpc_transport_stream_op *op) {
+    CallDataType *call_data = (CallDataType *)elem->call_data;
+    TransportStreamOp op_wrapper(op);
+    call_data->StartTransportStreamOp(exec_ctx, elem, &op_wrapper);
+  }
+
+  static void SetPollsetOrPollsetSet(grpc_exec_ctx *exec_ctx,
+                                     grpc_call_element *elem,
+                                     grpc_polling_entity *pollent) {
+    CallDataType *call_data = (CallDataType *)elem->call_data;
+    call_data->SetPollsetOrPollsetSet(exec_ctx, elem, pollent);
+  }
+
+  static char *GetPeer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {
+    CallDataType *call_data = (CallDataType *)elem->call_data;
+    return call_data->GetPeer(exec_ctx, elem);
+  }
+};
+
+struct FilterRecord {
+  grpc_channel_stack_type stack_type;
+  int priority;
+  std::function<bool(const grpc_channel_args &)> include_filter;
+  grpc_channel_filter filter;
+};
+extern std::vector<FilterRecord> *channel_filters;
+
+void ChannelFilterPluginInit();
+void ChannelFilterPluginShutdown();
+
+}  // namespace internal
+
+/// Registers a new filter.
+/// Must be called by only one thread at a time.
+/// The \a include_filter argument specifies a function that will be called
+/// to determine at run-time whether or not to add the filter. If the
+/// value is nullptr, the filter will be added unconditionally.
+template <typename ChannelDataType, typename CallDataType>
+void RegisterChannelFilter(
+    const char *name, grpc_channel_stack_type stack_type, int priority,
+    std::function<bool(const grpc_channel_args &)> include_filter) {
+  // If we haven't been called before, initialize channel_filters and
+  // call grpc_register_plugin().
+  if (internal::channel_filters == nullptr) {
+    grpc_register_plugin(internal::ChannelFilterPluginInit,
+                         internal::ChannelFilterPluginShutdown);
+    internal::channel_filters = new std::vector<internal::FilterRecord>();
+  }
+  // Add an entry to channel_filters. The filter will be added when the
+  // C-core initialization code calls ChannelFilterPluginInit().
+  typedef internal::ChannelFilter<ChannelDataType, CallDataType> FilterType;
+  internal::FilterRecord filter_record = {
+      stack_type,
+      priority,
+      include_filter,
+      {FilterType::StartTransportStreamOp, FilterType::StartTransportOp,
+       FilterType::call_data_size, FilterType::InitCallElement,
+       FilterType::SetPollsetOrPollsetSet, FilterType::DestroyCallElement,
+       FilterType::channel_data_size, FilterType::InitChannelElement,
+       FilterType::DestroyChannelElement, FilterType::GetPeer, name}};
+  internal::channel_filters->push_back(filter_record);
+}
+
+}  // namespace grpc
+
+#endif  // GRPCXX_CHANNEL_FILTER_H
diff --git a/src/cpp/common/completion_queue.cc b/src/cpp/common/completion_queue_cc.cc
similarity index 100%
rename from src/cpp/common/completion_queue.cc
rename to src/cpp/common/completion_queue_cc.cc
diff --git a/src/cpp/common/core_codegen.cc b/src/cpp/common/core_codegen.cc
index 3d6780b..ce02202 100644
--- a/src/cpp/common/core_codegen.cc
+++ b/src/cpp/common/core_codegen.cc
@@ -39,15 +39,19 @@
 #include <grpc/byte_buffer.h>
 #include <grpc/byte_buffer_reader.h>
 #include <grpc/grpc.h>
-#include <grpc/impl/codegen/alloc.h>
-#include <grpc/impl/codegen/byte_buffer.h>
-#include <grpc/impl/codegen/log.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
 #include <grpc/support/port_platform.h>
 #include <grpc/support/slice.h>
 #include <grpc/support/slice_buffer.h>
+#include <grpc/support/sync.h>
 
 #include "src/core/lib/profiling/timers.h"
 
+extern "C" {
+struct grpc_byte_buffer;
+}
+
 namespace grpc {
 
 grpc_completion_queue* CoreCodegen::grpc_completion_queue_create(
@@ -70,6 +74,19 @@
 
 void CoreCodegen::gpr_free(void* p) { return ::gpr_free(p); }
 
+void CoreCodegen::gpr_mu_init(gpr_mu* mu) { ::gpr_mu_init(mu); };
+void CoreCodegen::gpr_mu_destroy(gpr_mu* mu) { ::gpr_mu_destroy(mu); }
+void CoreCodegen::gpr_mu_lock(gpr_mu* mu) { ::gpr_mu_lock(mu); }
+void CoreCodegen::gpr_mu_unlock(gpr_mu* mu) { ::gpr_mu_unlock(mu); }
+void CoreCodegen::gpr_cv_init(gpr_cv* cv) { ::gpr_cv_init(cv); }
+void CoreCodegen::gpr_cv_destroy(gpr_cv* cv) { ::gpr_cv_destroy(cv); }
+int CoreCodegen::gpr_cv_wait(gpr_cv* cv, gpr_mu* mu,
+                             gpr_timespec abs_deadline) {
+  return ::gpr_cv_wait(cv, mu, abs_deadline);
+}
+void CoreCodegen::gpr_cv_signal(gpr_cv* cv) { ::gpr_cv_signal(cv); }
+void CoreCodegen::gpr_cv_broadcast(gpr_cv* cv) { ::gpr_cv_broadcast(cv); }
+
 void CoreCodegen::grpc_byte_buffer_destroy(grpc_byte_buffer* bb) {
   ::grpc_byte_buffer_destroy(bb);
 }
@@ -128,6 +145,10 @@
   return ::gpr_inf_future(type);
 }
 
+gpr_timespec CoreCodegen::gpr_time_0(gpr_clock_type type) {
+  return ::gpr_time_0(type);
+}
+
 void CoreCodegen::assert_fail(const char* failed_assertion) {
   gpr_log(GPR_ERROR, "assertion failed: %s", failed_assertion);
   abort();
diff --git a/src/cpp/ext/reflection.grpc.pb.cc b/src/cpp/ext/reflection.grpc.pb.cc
index b046dfc..8139c8e 100644
--- a/src/cpp/ext/reflection.grpc.pb.cc
+++ b/src/cpp/ext/reflection.grpc.pb.cc
@@ -32,7 +32,7 @@
  */
 
 
-// Generated by the gRPC protobuf plugin.
+// Generated by tools/codegen/extensions/gen_reflection_proto.sh
 // If you make any local change, they will be lost.
 // source: reflection.proto
 
diff --git a/src/cpp/ext/reflection.pb.cc b/src/cpp/ext/reflection.pb.cc
index b73a65d..d75503b 100644
--- a/src/cpp/ext/reflection.pb.cc
+++ b/src/cpp/ext/reflection.pb.cc
@@ -32,7 +32,7 @@
  */
 
 
-// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// Generated by tools/codegen/extensions/gen_reflection_proto.sh
 // source: reflection.proto
 
 #define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
@@ -98,6 +98,7 @@
 }  // namespace
 
 
+void protobuf_AssignDesc_reflection_2eproto() GOOGLE_ATTRIBUTE_COLD;
 void protobuf_AssignDesc_reflection_2eproto() {
   protobuf_AddDesc_reflection_2eproto();
   const ::google::protobuf::FileDescriptor* file =
@@ -117,7 +118,7 @@
   ServerReflectionRequest_reflection_ =
     ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection(
       ServerReflectionRequest_descriptor_,
-      ServerReflectionRequest::default_instance_,
+      ServerReflectionRequest::internal_default_instance(),
       ServerReflectionRequest_offsets_,
       -1,
       -1,
@@ -125,8 +126,7 @@
       ServerReflectionRequest_default_oneof_instance_,
       GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServerReflectionRequest, _oneof_case_[0]),
       sizeof(ServerReflectionRequest),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServerReflectionRequest, _internal_metadata_),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServerReflectionRequest, _is_default_instance_));
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServerReflectionRequest, _internal_metadata_));
   ExtensionRequest_descriptor_ = file->message_type(1);
   static const int ExtensionRequest_offsets_[2] = {
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ExtensionRequest, containing_type_),
@@ -135,14 +135,13 @@
   ExtensionRequest_reflection_ =
     ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection(
       ExtensionRequest_descriptor_,
-      ExtensionRequest::default_instance_,
+      ExtensionRequest::internal_default_instance(),
       ExtensionRequest_offsets_,
       -1,
       -1,
       -1,
       sizeof(ExtensionRequest),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ExtensionRequest, _internal_metadata_),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ExtensionRequest, _is_default_instance_));
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ExtensionRequest, _internal_metadata_));
   ServerReflectionResponse_descriptor_ = file->message_type(2);
   static const int ServerReflectionResponse_offsets_[7] = {
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServerReflectionResponse, valid_host_),
@@ -156,7 +155,7 @@
   ServerReflectionResponse_reflection_ =
     ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection(
       ServerReflectionResponse_descriptor_,
-      ServerReflectionResponse::default_instance_,
+      ServerReflectionResponse::internal_default_instance(),
       ServerReflectionResponse_offsets_,
       -1,
       -1,
@@ -164,8 +163,7 @@
       ServerReflectionResponse_default_oneof_instance_,
       GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServerReflectionResponse, _oneof_case_[0]),
       sizeof(ServerReflectionResponse),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServerReflectionResponse, _internal_metadata_),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServerReflectionResponse, _is_default_instance_));
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServerReflectionResponse, _internal_metadata_));
   FileDescriptorResponse_descriptor_ = file->message_type(3);
   static const int FileDescriptorResponse_offsets_[1] = {
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorResponse, file_descriptor_proto_),
@@ -173,14 +171,13 @@
   FileDescriptorResponse_reflection_ =
     ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection(
       FileDescriptorResponse_descriptor_,
-      FileDescriptorResponse::default_instance_,
+      FileDescriptorResponse::internal_default_instance(),
       FileDescriptorResponse_offsets_,
       -1,
       -1,
       -1,
       sizeof(FileDescriptorResponse),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorResponse, _internal_metadata_),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorResponse, _is_default_instance_));
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorResponse, _internal_metadata_));
   ExtensionNumberResponse_descriptor_ = file->message_type(4);
   static const int ExtensionNumberResponse_offsets_[2] = {
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ExtensionNumberResponse, base_type_name_),
@@ -189,14 +186,13 @@
   ExtensionNumberResponse_reflection_ =
     ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection(
       ExtensionNumberResponse_descriptor_,
-      ExtensionNumberResponse::default_instance_,
+      ExtensionNumberResponse::internal_default_instance(),
       ExtensionNumberResponse_offsets_,
       -1,
       -1,
       -1,
       sizeof(ExtensionNumberResponse),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ExtensionNumberResponse, _internal_metadata_),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ExtensionNumberResponse, _is_default_instance_));
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ExtensionNumberResponse, _internal_metadata_));
   ListServiceResponse_descriptor_ = file->message_type(5);
   static const int ListServiceResponse_offsets_[1] = {
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ListServiceResponse, service_),
@@ -204,14 +200,13 @@
   ListServiceResponse_reflection_ =
     ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection(
       ListServiceResponse_descriptor_,
-      ListServiceResponse::default_instance_,
+      ListServiceResponse::internal_default_instance(),
       ListServiceResponse_offsets_,
       -1,
       -1,
       -1,
       sizeof(ListServiceResponse),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ListServiceResponse, _internal_metadata_),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ListServiceResponse, _is_default_instance_));
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ListServiceResponse, _internal_metadata_));
   ServiceResponse_descriptor_ = file->message_type(6);
   static const int ServiceResponse_offsets_[1] = {
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceResponse, name_),
@@ -219,14 +214,13 @@
   ServiceResponse_reflection_ =
     ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection(
       ServiceResponse_descriptor_,
-      ServiceResponse::default_instance_,
+      ServiceResponse::internal_default_instance(),
       ServiceResponse_offsets_,
       -1,
       -1,
       -1,
       sizeof(ServiceResponse),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceResponse, _internal_metadata_),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceResponse, _is_default_instance_));
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceResponse, _internal_metadata_));
   ErrorResponse_descriptor_ = file->message_type(7);
   static const int ErrorResponse_offsets_[2] = {
     GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ErrorResponse, error_code_),
@@ -235,73 +229,106 @@
   ErrorResponse_reflection_ =
     ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection(
       ErrorResponse_descriptor_,
-      ErrorResponse::default_instance_,
+      ErrorResponse::internal_default_instance(),
       ErrorResponse_offsets_,
       -1,
       -1,
       -1,
       sizeof(ErrorResponse),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ErrorResponse, _internal_metadata_),
-      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ErrorResponse, _is_default_instance_));
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ErrorResponse, _internal_metadata_));
 }
 
 namespace {
 
 GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_);
-inline void protobuf_AssignDescriptorsOnce() {
+void protobuf_AssignDescriptorsOnce() {
   ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,
                  &protobuf_AssignDesc_reflection_2eproto);
 }
 
+void protobuf_RegisterTypes(const ::std::string&) GOOGLE_ATTRIBUTE_COLD;
 void protobuf_RegisterTypes(const ::std::string&) {
   protobuf_AssignDescriptorsOnce();
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
-      ServerReflectionRequest_descriptor_, &ServerReflectionRequest::default_instance());
+      ServerReflectionRequest_descriptor_, ServerReflectionRequest::internal_default_instance());
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
-      ExtensionRequest_descriptor_, &ExtensionRequest::default_instance());
+      ExtensionRequest_descriptor_, ExtensionRequest::internal_default_instance());
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
-      ServerReflectionResponse_descriptor_, &ServerReflectionResponse::default_instance());
+      ServerReflectionResponse_descriptor_, ServerReflectionResponse::internal_default_instance());
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
-      FileDescriptorResponse_descriptor_, &FileDescriptorResponse::default_instance());
+      FileDescriptorResponse_descriptor_, FileDescriptorResponse::internal_default_instance());
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
-      ExtensionNumberResponse_descriptor_, &ExtensionNumberResponse::default_instance());
+      ExtensionNumberResponse_descriptor_, ExtensionNumberResponse::internal_default_instance());
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
-      ListServiceResponse_descriptor_, &ListServiceResponse::default_instance());
+      ListServiceResponse_descriptor_, ListServiceResponse::internal_default_instance());
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
-      ServiceResponse_descriptor_, &ServiceResponse::default_instance());
+      ServiceResponse_descriptor_, ServiceResponse::internal_default_instance());
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
-      ErrorResponse_descriptor_, &ErrorResponse::default_instance());
+      ErrorResponse_descriptor_, ErrorResponse::internal_default_instance());
 }
 
 }  // namespace
 
 void protobuf_ShutdownFile_reflection_2eproto() {
-  delete ServerReflectionRequest::default_instance_;
+  ServerReflectionRequest_default_instance_.Shutdown();
   delete ServerReflectionRequest_default_oneof_instance_;
   delete ServerReflectionRequest_reflection_;
-  delete ExtensionRequest::default_instance_;
+  ExtensionRequest_default_instance_.Shutdown();
   delete ExtensionRequest_reflection_;
-  delete ServerReflectionResponse::default_instance_;
+  ServerReflectionResponse_default_instance_.Shutdown();
   delete ServerReflectionResponse_default_oneof_instance_;
   delete ServerReflectionResponse_reflection_;
-  delete FileDescriptorResponse::default_instance_;
+  FileDescriptorResponse_default_instance_.Shutdown();
   delete FileDescriptorResponse_reflection_;
-  delete ExtensionNumberResponse::default_instance_;
+  ExtensionNumberResponse_default_instance_.Shutdown();
   delete ExtensionNumberResponse_reflection_;
-  delete ListServiceResponse::default_instance_;
+  ListServiceResponse_default_instance_.Shutdown();
   delete ListServiceResponse_reflection_;
-  delete ServiceResponse::default_instance_;
+  ServiceResponse_default_instance_.Shutdown();
   delete ServiceResponse_reflection_;
-  delete ErrorResponse::default_instance_;
+  ErrorResponse_default_instance_.Shutdown();
   delete ErrorResponse_reflection_;
 }
 
-void protobuf_AddDesc_reflection_2eproto() {
-  static bool already_here = false;
-  if (already_here) return;
-  already_here = true;
+void protobuf_InitDefaults_reflection_2eproto_impl() {
   GOOGLE_PROTOBUF_VERIFY_VERSION;
 
+  ::google::protobuf::internal::GetEmptyString();
+  ServerReflectionRequest_default_instance_.DefaultConstruct();
+  ServerReflectionRequest_default_oneof_instance_ = new ServerReflectionRequestOneofInstance();
+  ::google::protobuf::internal::GetEmptyString();
+  ExtensionRequest_default_instance_.DefaultConstruct();
+  ::google::protobuf::internal::GetEmptyString();
+  ServerReflectionResponse_default_instance_.DefaultConstruct();
+  ServerReflectionResponse_default_oneof_instance_ = new ServerReflectionResponseOneofInstance();
+  ::google::protobuf::internal::GetEmptyString();
+  FileDescriptorResponse_default_instance_.DefaultConstruct();
+  ::google::protobuf::internal::GetEmptyString();
+  ExtensionNumberResponse_default_instance_.DefaultConstruct();
+  ListServiceResponse_default_instance_.DefaultConstruct();
+  ::google::protobuf::internal::GetEmptyString();
+  ServiceResponse_default_instance_.DefaultConstruct();
+  ::google::protobuf::internal::GetEmptyString();
+  ErrorResponse_default_instance_.DefaultConstruct();
+  ServerReflectionRequest_default_instance_.get_mutable()->InitAsDefaultInstance();
+  ExtensionRequest_default_instance_.get_mutable()->InitAsDefaultInstance();
+  ServerReflectionResponse_default_instance_.get_mutable()->InitAsDefaultInstance();
+  FileDescriptorResponse_default_instance_.get_mutable()->InitAsDefaultInstance();
+  ExtensionNumberResponse_default_instance_.get_mutable()->InitAsDefaultInstance();
+  ListServiceResponse_default_instance_.get_mutable()->InitAsDefaultInstance();
+  ServiceResponse_default_instance_.get_mutable()->InitAsDefaultInstance();
+  ErrorResponse_default_instance_.get_mutable()->InitAsDefaultInstance();
+}
+
+GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_InitDefaults_reflection_2eproto_once_);
+void protobuf_InitDefaults_reflection_2eproto() {
+  ::google::protobuf::GoogleOnceInit(&protobuf_InitDefaults_reflection_2eproto_once_,
+                 &protobuf_InitDefaults_reflection_2eproto_impl);
+}
+void protobuf_AddDesc_reflection_2eproto_impl() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  protobuf_InitDefaults_reflection_2eproto();
   ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
     "\n\020reflection.proto\022\027grpc.reflection.v1al"
     "pha\"\212\002\n\027ServerReflectionRequest\022\014\n\004host\030"
@@ -338,27 +365,14 @@
     "a.ServerReflectionResponse(\0010\001b\006proto3", 1318);
   ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
     "reflection.proto", &protobuf_RegisterTypes);
-  ServerReflectionRequest::default_instance_ = new ServerReflectionRequest();
-  ServerReflectionRequest_default_oneof_instance_ = new ServerReflectionRequestOneofInstance();
-  ExtensionRequest::default_instance_ = new ExtensionRequest();
-  ServerReflectionResponse::default_instance_ = new ServerReflectionResponse();
-  ServerReflectionResponse_default_oneof_instance_ = new ServerReflectionResponseOneofInstance();
-  FileDescriptorResponse::default_instance_ = new FileDescriptorResponse();
-  ExtensionNumberResponse::default_instance_ = new ExtensionNumberResponse();
-  ListServiceResponse::default_instance_ = new ListServiceResponse();
-  ServiceResponse::default_instance_ = new ServiceResponse();
-  ErrorResponse::default_instance_ = new ErrorResponse();
-  ServerReflectionRequest::default_instance_->InitAsDefaultInstance();
-  ExtensionRequest::default_instance_->InitAsDefaultInstance();
-  ServerReflectionResponse::default_instance_->InitAsDefaultInstance();
-  FileDescriptorResponse::default_instance_->InitAsDefaultInstance();
-  ExtensionNumberResponse::default_instance_->InitAsDefaultInstance();
-  ListServiceResponse::default_instance_->InitAsDefaultInstance();
-  ServiceResponse::default_instance_->InitAsDefaultInstance();
-  ErrorResponse::default_instance_->InitAsDefaultInstance();
   ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_reflection_2eproto);
 }
 
+GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AddDesc_reflection_2eproto_once_);
+void protobuf_AddDesc_reflection_2eproto() {
+  ::google::protobuf::GoogleOnceInit(&protobuf_AddDesc_reflection_2eproto_once_,
+                 &protobuf_AddDesc_reflection_2eproto_impl);
+}
 // Force AddDescriptors() to be called at static initialization time.
 struct StaticDescriptorInitializer_reflection_2eproto {
   StaticDescriptorInitializer_reflection_2eproto() {
@@ -368,9 +382,9 @@
 
 namespace {
 
-static void MergeFromFail(int line) GOOGLE_ATTRIBUTE_COLD;
+static void MergeFromFail(int line) GOOGLE_ATTRIBUTE_COLD GOOGLE_ATTRIBUTE_NORETURN;
 static void MergeFromFail(int line) {
-  GOOGLE_CHECK(false) << __FILE__ << ":" << line;
+  ::google::protobuf::internal::MergeFromFail(__FILE__, line);
 }
 
 }  // namespace
@@ -389,15 +403,16 @@
 
 ServerReflectionRequest::ServerReflectionRequest()
   : ::google::protobuf::Message(), _internal_metadata_(NULL) {
+  if (this != internal_default_instance()) protobuf_InitDefaults_reflection_2eproto();
   SharedCtor();
   // @@protoc_insertion_point(constructor:grpc.reflection.v1alpha.ServerReflectionRequest)
 }
 
 void ServerReflectionRequest::InitAsDefaultInstance() {
-  _is_default_instance_ = true;
   ServerReflectionRequest_default_oneof_instance_->file_by_filename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   ServerReflectionRequest_default_oneof_instance_->file_containing_symbol_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
-  ServerReflectionRequest_default_oneof_instance_->file_containing_extension_ = const_cast< ::grpc::reflection::v1alpha::ExtensionRequest*>(&::grpc::reflection::v1alpha::ExtensionRequest::default_instance());
+  ServerReflectionRequest_default_oneof_instance_->file_containing_extension_ = const_cast< ::grpc::reflection::v1alpha::ExtensionRequest*>(
+      ::grpc::reflection::v1alpha::ExtensionRequest::internal_default_instance());
   ServerReflectionRequest_default_oneof_instance_->all_extension_numbers_of_type_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   ServerReflectionRequest_default_oneof_instance_->list_services_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
@@ -406,16 +421,14 @@
   : ::google::protobuf::Message(),
     _internal_metadata_(NULL) {
   SharedCtor();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
   // @@protoc_insertion_point(copy_constructor:grpc.reflection.v1alpha.ServerReflectionRequest)
 }
 
 void ServerReflectionRequest::SharedCtor() {
-    _is_default_instance_ = false;
-  ::google::protobuf::internal::GetEmptyString();
-  _cached_size_ = 0;
   host_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   clear_has_message_request();
+  _cached_size_ = 0;
 }
 
 ServerReflectionRequest::~ServerReflectionRequest() {
@@ -428,8 +441,6 @@
   if (has_message_request()) {
     clear_message_request();
   }
-  if (this != default_instance_) {
-  }
 }
 
 void ServerReflectionRequest::SetCachedSize(int size) const {
@@ -443,11 +454,11 @@
 }
 
 const ServerReflectionRequest& ServerReflectionRequest::default_instance() {
-  if (default_instance_ == NULL) protobuf_AddDesc_reflection_2eproto();
-  return *default_instance_;
+  protobuf_InitDefaults_reflection_2eproto();
+  return *internal_default_instance();
 }
 
-ServerReflectionRequest* ServerReflectionRequest::default_instance_ = NULL;
+::google::protobuf::internal::ExplicitlyConstructed<ServerReflectionRequest> ServerReflectionRequest_default_instance_;
 
 ServerReflectionRequest* ServerReflectionRequest::New(::google::protobuf::Arena* arena) const {
   ServerReflectionRequest* n = new ServerReflectionRequest;
@@ -459,7 +470,7 @@
 
 void ServerReflectionRequest::clear_message_request() {
 // @@protoc_insertion_point(one_of_clear_start:grpc.reflection.v1alpha.ServerReflectionRequest)
-  switch(message_request_case()) {
+  switch (message_request_case()) {
     case kFileByFilename: {
       message_request_.file_by_filename_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
       break;
@@ -533,14 +544,13 @@
         } else {
           goto handle_unusual;
         }
-        if (input->ExpectTag(34)) goto parse_file_containing_symbol;
+        goto after_list_services;
         break;
       }
 
       // optional string file_containing_symbol = 4;
       case 4: {
         if (tag == 34) {
-         parse_file_containing_symbol:
           DO_(::google::protobuf::internal::WireFormatLite::ReadString(
                 input, this->mutable_file_containing_symbol()));
           DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
@@ -550,27 +560,25 @@
         } else {
           goto handle_unusual;
         }
-        if (input->ExpectTag(42)) goto parse_file_containing_extension;
+        goto after_list_services;
         break;
       }
 
       // optional .grpc.reflection.v1alpha.ExtensionRequest file_containing_extension = 5;
       case 5: {
         if (tag == 42) {
-         parse_file_containing_extension:
           DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
                input, mutable_file_containing_extension()));
         } else {
           goto handle_unusual;
         }
-        if (input->ExpectTag(50)) goto parse_all_extension_numbers_of_type;
+        goto after_list_services;
         break;
       }
 
       // optional string all_extension_numbers_of_type = 6;
       case 6: {
         if (tag == 50) {
-         parse_all_extension_numbers_of_type:
           DO_(::google::protobuf::internal::WireFormatLite::ReadString(
                 input, this->mutable_all_extension_numbers_of_type()));
           DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
@@ -597,6 +605,7 @@
         } else {
           goto handle_unusual;
         }
+       after_list_services:
         if (input->ExpectAtEnd()) goto success;
         break;
       }
@@ -684,8 +693,9 @@
   // @@protoc_insertion_point(serialize_end:grpc.reflection.v1alpha.ServerReflectionRequest)
 }
 
-::google::protobuf::uint8* ServerReflectionRequest::SerializeWithCachedSizesToArray(
-    ::google::protobuf::uint8* target) const {
+::google::protobuf::uint8* ServerReflectionRequest::InternalSerializeWithCachedSizesToArray(
+    bool deterministic, ::google::protobuf::uint8* target) const {
+  (void)deterministic; // Unused
   // @@protoc_insertion_point(serialize_to_array_start:grpc.reflection.v1alpha.ServerReflectionRequest)
   // optional string host = 1;
   if (this->host().size() > 0) {
@@ -723,8 +733,8 @@
   // optional .grpc.reflection.v1alpha.ExtensionRequest file_containing_extension = 5;
   if (has_file_containing_extension()) {
     target = ::google::protobuf::internal::WireFormatLite::
-      WriteMessageNoVirtualToArray(
-        5, *message_request_.file_containing_extension_, target);
+      InternalWriteMessageNoVirtualToArray(
+        5, *message_request_.file_containing_extension_, false, target);
   }
 
   // optional string all_extension_numbers_of_type = 6;
@@ -753,9 +763,9 @@
   return target;
 }
 
-int ServerReflectionRequest::ByteSize() const {
+size_t ServerReflectionRequest::ByteSizeLong() const {
 // @@protoc_insertion_point(message_byte_size_start:grpc.reflection.v1alpha.ServerReflectionRequest)
-  int total_size = 0;
+  size_t total_size = 0;
 
   // optional string host = 1;
   if (this->host().size() > 0) {
@@ -804,8 +814,9 @@
       break;
     }
   }
+  int cached_size = ::google::protobuf::internal::ToCachedSize(total_size);
   GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
-  _cached_size_ = total_size;
+  _cached_size_ = cached_size;
   GOOGLE_SAFE_CONCURRENT_WRITES_END();
   return total_size;
 }
@@ -813,7 +824,7 @@
 void ServerReflectionRequest::MergeFrom(const ::google::protobuf::Message& from) {
 // @@protoc_insertion_point(generalized_merge_from_start:grpc.reflection.v1alpha.ServerReflectionRequest)
   if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
-  const ServerReflectionRequest* source = 
+  const ServerReflectionRequest* source =
       ::google::protobuf::internal::DynamicCastToGenerated<const ServerReflectionRequest>(
           &from);
   if (source == NULL) {
@@ -821,13 +832,21 @@
     ::google::protobuf::internal::ReflectionOps::Merge(from, this);
   } else {
   // @@protoc_insertion_point(generalized_merge_from_cast_success:grpc.reflection.v1alpha.ServerReflectionRequest)
-    MergeFrom(*source);
+    UnsafeMergeFrom(*source);
   }
 }
 
 void ServerReflectionRequest::MergeFrom(const ServerReflectionRequest& from) {
 // @@protoc_insertion_point(class_specific_merge_from_start:grpc.reflection.v1alpha.ServerReflectionRequest)
-  if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+  if (GOOGLE_PREDICT_TRUE(&from != this)) {
+    UnsafeMergeFrom(from);
+  } else {
+    MergeFromFail(__LINE__);
+  }
+}
+
+void ServerReflectionRequest::UnsafeMergeFrom(const ServerReflectionRequest& from) {
+  GOOGLE_DCHECK(&from != this);
   switch (from.message_request_case()) {
     case kFileByFilename: {
       set_file_by_filename(from.file_by_filename());
@@ -870,7 +889,7 @@
 // @@protoc_insertion_point(class_specific_copy_from_start:grpc.reflection.v1alpha.ServerReflectionRequest)
   if (&from == this) return;
   Clear();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
 }
 
 bool ServerReflectionRequest::IsInitialized() const {
@@ -905,37 +924,37 @@
 void ServerReflectionRequest::clear_host() {
   host_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- const ::std::string& ServerReflectionRequest::host() const {
+const ::std::string& ServerReflectionRequest::host() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ServerReflectionRequest.host)
   return host_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- void ServerReflectionRequest::set_host(const ::std::string& value) {
+void ServerReflectionRequest::set_host(const ::std::string& value) {
   
   host_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ServerReflectionRequest.host)
 }
- void ServerReflectionRequest::set_host(const char* value) {
+void ServerReflectionRequest::set_host(const char* value) {
   
   host_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
   // @@protoc_insertion_point(field_set_char:grpc.reflection.v1alpha.ServerReflectionRequest.host)
 }
- void ServerReflectionRequest::set_host(const char* value, size_t size) {
+void ServerReflectionRequest::set_host(const char* value, size_t size) {
   
   host_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
       ::std::string(reinterpret_cast<const char*>(value), size));
   // @@protoc_insertion_point(field_set_pointer:grpc.reflection.v1alpha.ServerReflectionRequest.host)
 }
- ::std::string* ServerReflectionRequest::mutable_host() {
+::std::string* ServerReflectionRequest::mutable_host() {
   
   // @@protoc_insertion_point(field_mutable:grpc.reflection.v1alpha.ServerReflectionRequest.host)
   return host_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- ::std::string* ServerReflectionRequest::release_host() {
+::std::string* ServerReflectionRequest::release_host() {
   // @@protoc_insertion_point(field_release:grpc.reflection.v1alpha.ServerReflectionRequest.host)
   
   return host_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- void ServerReflectionRequest::set_allocated_host(::std::string* host) {
+void ServerReflectionRequest::set_allocated_host(::std::string* host) {
   if (host != NULL) {
     
   } else {
@@ -958,14 +977,14 @@
     clear_has_message_request();
   }
 }
- const ::std::string& ServerReflectionRequest::file_by_filename() const {
+const ::std::string& ServerReflectionRequest::file_by_filename() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ServerReflectionRequest.file_by_filename)
   if (has_file_by_filename()) {
     return message_request_.file_by_filename_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   }
   return *&::google::protobuf::internal::GetEmptyStringAlreadyInited();
 }
- void ServerReflectionRequest::set_file_by_filename(const ::std::string& value) {
+void ServerReflectionRequest::set_file_by_filename(const ::std::string& value) {
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ServerReflectionRequest.file_by_filename)
   if (!has_file_by_filename()) {
     clear_message_request();
@@ -975,7 +994,7 @@
   message_request_.file_by_filename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ServerReflectionRequest.file_by_filename)
 }
- void ServerReflectionRequest::set_file_by_filename(const char* value) {
+void ServerReflectionRequest::set_file_by_filename(const char* value) {
   if (!has_file_by_filename()) {
     clear_message_request();
     set_has_file_by_filename();
@@ -985,7 +1004,7 @@
       ::std::string(value));
   // @@protoc_insertion_point(field_set_char:grpc.reflection.v1alpha.ServerReflectionRequest.file_by_filename)
 }
- void ServerReflectionRequest::set_file_by_filename(const char* value, size_t size) {
+void ServerReflectionRequest::set_file_by_filename(const char* value, size_t size) {
   if (!has_file_by_filename()) {
     clear_message_request();
     set_has_file_by_filename();
@@ -995,7 +1014,7 @@
       reinterpret_cast<const char*>(value), size));
   // @@protoc_insertion_point(field_set_pointer:grpc.reflection.v1alpha.ServerReflectionRequest.file_by_filename)
 }
- ::std::string* ServerReflectionRequest::mutable_file_by_filename() {
+::std::string* ServerReflectionRequest::mutable_file_by_filename() {
   if (!has_file_by_filename()) {
     clear_message_request();
     set_has_file_by_filename();
@@ -1004,7 +1023,7 @@
   // @@protoc_insertion_point(field_mutable:grpc.reflection.v1alpha.ServerReflectionRequest.file_by_filename)
   return message_request_.file_by_filename_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- ::std::string* ServerReflectionRequest::release_file_by_filename() {
+::std::string* ServerReflectionRequest::release_file_by_filename() {
   // @@protoc_insertion_point(field_release:grpc.reflection.v1alpha.ServerReflectionRequest.file_by_filename)
   if (has_file_by_filename()) {
     clear_has_message_request();
@@ -1013,7 +1032,7 @@
     return NULL;
   }
 }
- void ServerReflectionRequest::set_allocated_file_by_filename(::std::string* file_by_filename) {
+void ServerReflectionRequest::set_allocated_file_by_filename(::std::string* file_by_filename) {
   if (!has_file_by_filename()) {
     message_request_.file_by_filename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   }
@@ -1039,14 +1058,14 @@
     clear_has_message_request();
   }
 }
- const ::std::string& ServerReflectionRequest::file_containing_symbol() const {
+const ::std::string& ServerReflectionRequest::file_containing_symbol() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ServerReflectionRequest.file_containing_symbol)
   if (has_file_containing_symbol()) {
     return message_request_.file_containing_symbol_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   }
   return *&::google::protobuf::internal::GetEmptyStringAlreadyInited();
 }
- void ServerReflectionRequest::set_file_containing_symbol(const ::std::string& value) {
+void ServerReflectionRequest::set_file_containing_symbol(const ::std::string& value) {
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ServerReflectionRequest.file_containing_symbol)
   if (!has_file_containing_symbol()) {
     clear_message_request();
@@ -1056,7 +1075,7 @@
   message_request_.file_containing_symbol_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ServerReflectionRequest.file_containing_symbol)
 }
- void ServerReflectionRequest::set_file_containing_symbol(const char* value) {
+void ServerReflectionRequest::set_file_containing_symbol(const char* value) {
   if (!has_file_containing_symbol()) {
     clear_message_request();
     set_has_file_containing_symbol();
@@ -1066,7 +1085,7 @@
       ::std::string(value));
   // @@protoc_insertion_point(field_set_char:grpc.reflection.v1alpha.ServerReflectionRequest.file_containing_symbol)
 }
- void ServerReflectionRequest::set_file_containing_symbol(const char* value, size_t size) {
+void ServerReflectionRequest::set_file_containing_symbol(const char* value, size_t size) {
   if (!has_file_containing_symbol()) {
     clear_message_request();
     set_has_file_containing_symbol();
@@ -1076,7 +1095,7 @@
       reinterpret_cast<const char*>(value), size));
   // @@protoc_insertion_point(field_set_pointer:grpc.reflection.v1alpha.ServerReflectionRequest.file_containing_symbol)
 }
- ::std::string* ServerReflectionRequest::mutable_file_containing_symbol() {
+::std::string* ServerReflectionRequest::mutable_file_containing_symbol() {
   if (!has_file_containing_symbol()) {
     clear_message_request();
     set_has_file_containing_symbol();
@@ -1085,7 +1104,7 @@
   // @@protoc_insertion_point(field_mutable:grpc.reflection.v1alpha.ServerReflectionRequest.file_containing_symbol)
   return message_request_.file_containing_symbol_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- ::std::string* ServerReflectionRequest::release_file_containing_symbol() {
+::std::string* ServerReflectionRequest::release_file_containing_symbol() {
   // @@protoc_insertion_point(field_release:grpc.reflection.v1alpha.ServerReflectionRequest.file_containing_symbol)
   if (has_file_containing_symbol()) {
     clear_has_message_request();
@@ -1094,7 +1113,7 @@
     return NULL;
   }
 }
- void ServerReflectionRequest::set_allocated_file_containing_symbol(::std::string* file_containing_symbol) {
+void ServerReflectionRequest::set_allocated_file_containing_symbol(::std::string* file_containing_symbol) {
   if (!has_file_containing_symbol()) {
     message_request_.file_containing_symbol_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   }
@@ -1168,14 +1187,14 @@
     clear_has_message_request();
   }
 }
- const ::std::string& ServerReflectionRequest::all_extension_numbers_of_type() const {
+const ::std::string& ServerReflectionRequest::all_extension_numbers_of_type() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ServerReflectionRequest.all_extension_numbers_of_type)
   if (has_all_extension_numbers_of_type()) {
     return message_request_.all_extension_numbers_of_type_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   }
   return *&::google::protobuf::internal::GetEmptyStringAlreadyInited();
 }
- void ServerReflectionRequest::set_all_extension_numbers_of_type(const ::std::string& value) {
+void ServerReflectionRequest::set_all_extension_numbers_of_type(const ::std::string& value) {
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ServerReflectionRequest.all_extension_numbers_of_type)
   if (!has_all_extension_numbers_of_type()) {
     clear_message_request();
@@ -1185,7 +1204,7 @@
   message_request_.all_extension_numbers_of_type_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ServerReflectionRequest.all_extension_numbers_of_type)
 }
- void ServerReflectionRequest::set_all_extension_numbers_of_type(const char* value) {
+void ServerReflectionRequest::set_all_extension_numbers_of_type(const char* value) {
   if (!has_all_extension_numbers_of_type()) {
     clear_message_request();
     set_has_all_extension_numbers_of_type();
@@ -1195,7 +1214,7 @@
       ::std::string(value));
   // @@protoc_insertion_point(field_set_char:grpc.reflection.v1alpha.ServerReflectionRequest.all_extension_numbers_of_type)
 }
- void ServerReflectionRequest::set_all_extension_numbers_of_type(const char* value, size_t size) {
+void ServerReflectionRequest::set_all_extension_numbers_of_type(const char* value, size_t size) {
   if (!has_all_extension_numbers_of_type()) {
     clear_message_request();
     set_has_all_extension_numbers_of_type();
@@ -1205,7 +1224,7 @@
       reinterpret_cast<const char*>(value), size));
   // @@protoc_insertion_point(field_set_pointer:grpc.reflection.v1alpha.ServerReflectionRequest.all_extension_numbers_of_type)
 }
- ::std::string* ServerReflectionRequest::mutable_all_extension_numbers_of_type() {
+::std::string* ServerReflectionRequest::mutable_all_extension_numbers_of_type() {
   if (!has_all_extension_numbers_of_type()) {
     clear_message_request();
     set_has_all_extension_numbers_of_type();
@@ -1214,7 +1233,7 @@
   // @@protoc_insertion_point(field_mutable:grpc.reflection.v1alpha.ServerReflectionRequest.all_extension_numbers_of_type)
   return message_request_.all_extension_numbers_of_type_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- ::std::string* ServerReflectionRequest::release_all_extension_numbers_of_type() {
+::std::string* ServerReflectionRequest::release_all_extension_numbers_of_type() {
   // @@protoc_insertion_point(field_release:grpc.reflection.v1alpha.ServerReflectionRequest.all_extension_numbers_of_type)
   if (has_all_extension_numbers_of_type()) {
     clear_has_message_request();
@@ -1223,7 +1242,7 @@
     return NULL;
   }
 }
- void ServerReflectionRequest::set_allocated_all_extension_numbers_of_type(::std::string* all_extension_numbers_of_type) {
+void ServerReflectionRequest::set_allocated_all_extension_numbers_of_type(::std::string* all_extension_numbers_of_type) {
   if (!has_all_extension_numbers_of_type()) {
     message_request_.all_extension_numbers_of_type_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   }
@@ -1249,14 +1268,14 @@
     clear_has_message_request();
   }
 }
- const ::std::string& ServerReflectionRequest::list_services() const {
+const ::std::string& ServerReflectionRequest::list_services() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ServerReflectionRequest.list_services)
   if (has_list_services()) {
     return message_request_.list_services_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   }
   return *&::google::protobuf::internal::GetEmptyStringAlreadyInited();
 }
- void ServerReflectionRequest::set_list_services(const ::std::string& value) {
+void ServerReflectionRequest::set_list_services(const ::std::string& value) {
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ServerReflectionRequest.list_services)
   if (!has_list_services()) {
     clear_message_request();
@@ -1266,7 +1285,7 @@
   message_request_.list_services_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ServerReflectionRequest.list_services)
 }
- void ServerReflectionRequest::set_list_services(const char* value) {
+void ServerReflectionRequest::set_list_services(const char* value) {
   if (!has_list_services()) {
     clear_message_request();
     set_has_list_services();
@@ -1276,7 +1295,7 @@
       ::std::string(value));
   // @@protoc_insertion_point(field_set_char:grpc.reflection.v1alpha.ServerReflectionRequest.list_services)
 }
- void ServerReflectionRequest::set_list_services(const char* value, size_t size) {
+void ServerReflectionRequest::set_list_services(const char* value, size_t size) {
   if (!has_list_services()) {
     clear_message_request();
     set_has_list_services();
@@ -1286,7 +1305,7 @@
       reinterpret_cast<const char*>(value), size));
   // @@protoc_insertion_point(field_set_pointer:grpc.reflection.v1alpha.ServerReflectionRequest.list_services)
 }
- ::std::string* ServerReflectionRequest::mutable_list_services() {
+::std::string* ServerReflectionRequest::mutable_list_services() {
   if (!has_list_services()) {
     clear_message_request();
     set_has_list_services();
@@ -1295,7 +1314,7 @@
   // @@protoc_insertion_point(field_mutable:grpc.reflection.v1alpha.ServerReflectionRequest.list_services)
   return message_request_.list_services_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- ::std::string* ServerReflectionRequest::release_list_services() {
+::std::string* ServerReflectionRequest::release_list_services() {
   // @@protoc_insertion_point(field_release:grpc.reflection.v1alpha.ServerReflectionRequest.list_services)
   if (has_list_services()) {
     clear_has_message_request();
@@ -1304,7 +1323,7 @@
     return NULL;
   }
 }
- void ServerReflectionRequest::set_allocated_list_services(::std::string* list_services) {
+void ServerReflectionRequest::set_allocated_list_services(::std::string* list_services) {
   if (!has_list_services()) {
     message_request_.list_services_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   }
@@ -1326,6 +1345,9 @@
 ServerReflectionRequest::MessageRequestCase ServerReflectionRequest::message_request_case() const {
   return ServerReflectionRequest::MessageRequestCase(_oneof_case_[0]);
 }
+inline const ServerReflectionRequest* ServerReflectionRequest::internal_default_instance() {
+  return &ServerReflectionRequest_default_instance_.get();
+}
 #endif  // PROTOBUF_INLINE_NOT_IN_HEADERS
 
 // ===================================================================
@@ -1337,28 +1359,26 @@
 
 ExtensionRequest::ExtensionRequest()
   : ::google::protobuf::Message(), _internal_metadata_(NULL) {
+  if (this != internal_default_instance()) protobuf_InitDefaults_reflection_2eproto();
   SharedCtor();
   // @@protoc_insertion_point(constructor:grpc.reflection.v1alpha.ExtensionRequest)
 }
 
 void ExtensionRequest::InitAsDefaultInstance() {
-  _is_default_instance_ = true;
 }
 
 ExtensionRequest::ExtensionRequest(const ExtensionRequest& from)
   : ::google::protobuf::Message(),
     _internal_metadata_(NULL) {
   SharedCtor();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
   // @@protoc_insertion_point(copy_constructor:grpc.reflection.v1alpha.ExtensionRequest)
 }
 
 void ExtensionRequest::SharedCtor() {
-    _is_default_instance_ = false;
-  ::google::protobuf::internal::GetEmptyString();
-  _cached_size_ = 0;
   containing_type_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   extension_number_ = 0;
+  _cached_size_ = 0;
 }
 
 ExtensionRequest::~ExtensionRequest() {
@@ -1368,8 +1388,6 @@
 
 void ExtensionRequest::SharedDtor() {
   containing_type_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
-  if (this != default_instance_) {
-  }
 }
 
 void ExtensionRequest::SetCachedSize(int size) const {
@@ -1383,11 +1401,11 @@
 }
 
 const ExtensionRequest& ExtensionRequest::default_instance() {
-  if (default_instance_ == NULL) protobuf_AddDesc_reflection_2eproto();
-  return *default_instance_;
+  protobuf_InitDefaults_reflection_2eproto();
+  return *internal_default_instance();
 }
 
-ExtensionRequest* ExtensionRequest::default_instance_ = NULL;
+::google::protobuf::internal::ExplicitlyConstructed<ExtensionRequest> ExtensionRequest_default_instance_;
 
 ExtensionRequest* ExtensionRequest::New(::google::protobuf::Arena* arena) const {
   ExtensionRequest* n = new ExtensionRequest;
@@ -1433,10 +1451,10 @@
       case 2: {
         if (tag == 16) {
          parse_extension_number:
+
           DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
                    ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
                  input, &extension_number_)));
-
         } else {
           goto handle_unusual;
         }
@@ -1486,8 +1504,9 @@
   // @@protoc_insertion_point(serialize_end:grpc.reflection.v1alpha.ExtensionRequest)
 }
 
-::google::protobuf::uint8* ExtensionRequest::SerializeWithCachedSizesToArray(
-    ::google::protobuf::uint8* target) const {
+::google::protobuf::uint8* ExtensionRequest::InternalSerializeWithCachedSizesToArray(
+    bool deterministic, ::google::protobuf::uint8* target) const {
+  (void)deterministic; // Unused
   // @@protoc_insertion_point(serialize_to_array_start:grpc.reflection.v1alpha.ExtensionRequest)
   // optional string containing_type = 1;
   if (this->containing_type().size() > 0) {
@@ -1509,9 +1528,9 @@
   return target;
 }
 
-int ExtensionRequest::ByteSize() const {
+size_t ExtensionRequest::ByteSizeLong() const {
 // @@protoc_insertion_point(message_byte_size_start:grpc.reflection.v1alpha.ExtensionRequest)
-  int total_size = 0;
+  size_t total_size = 0;
 
   // optional string containing_type = 1;
   if (this->containing_type().size() > 0) {
@@ -1527,8 +1546,9 @@
         this->extension_number());
   }
 
+  int cached_size = ::google::protobuf::internal::ToCachedSize(total_size);
   GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
-  _cached_size_ = total_size;
+  _cached_size_ = cached_size;
   GOOGLE_SAFE_CONCURRENT_WRITES_END();
   return total_size;
 }
@@ -1536,7 +1556,7 @@
 void ExtensionRequest::MergeFrom(const ::google::protobuf::Message& from) {
 // @@protoc_insertion_point(generalized_merge_from_start:grpc.reflection.v1alpha.ExtensionRequest)
   if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
-  const ExtensionRequest* source = 
+  const ExtensionRequest* source =
       ::google::protobuf::internal::DynamicCastToGenerated<const ExtensionRequest>(
           &from);
   if (source == NULL) {
@@ -1544,13 +1564,21 @@
     ::google::protobuf::internal::ReflectionOps::Merge(from, this);
   } else {
   // @@protoc_insertion_point(generalized_merge_from_cast_success:grpc.reflection.v1alpha.ExtensionRequest)
-    MergeFrom(*source);
+    UnsafeMergeFrom(*source);
   }
 }
 
 void ExtensionRequest::MergeFrom(const ExtensionRequest& from) {
 // @@protoc_insertion_point(class_specific_merge_from_start:grpc.reflection.v1alpha.ExtensionRequest)
-  if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+  if (GOOGLE_PREDICT_TRUE(&from != this)) {
+    UnsafeMergeFrom(from);
+  } else {
+    MergeFromFail(__LINE__);
+  }
+}
+
+void ExtensionRequest::UnsafeMergeFrom(const ExtensionRequest& from) {
+  GOOGLE_DCHECK(&from != this);
   if (from.containing_type().size() > 0) {
 
     containing_type_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.containing_type_);
@@ -1571,7 +1599,7 @@
 // @@protoc_insertion_point(class_specific_copy_from_start:grpc.reflection.v1alpha.ExtensionRequest)
   if (&from == this) return;
   Clear();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
 }
 
 bool ExtensionRequest::IsInitialized() const {
@@ -1605,37 +1633,37 @@
 void ExtensionRequest::clear_containing_type() {
   containing_type_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- const ::std::string& ExtensionRequest::containing_type() const {
+const ::std::string& ExtensionRequest::containing_type() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ExtensionRequest.containing_type)
   return containing_type_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- void ExtensionRequest::set_containing_type(const ::std::string& value) {
+void ExtensionRequest::set_containing_type(const ::std::string& value) {
   
   containing_type_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ExtensionRequest.containing_type)
 }
- void ExtensionRequest::set_containing_type(const char* value) {
+void ExtensionRequest::set_containing_type(const char* value) {
   
   containing_type_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
   // @@protoc_insertion_point(field_set_char:grpc.reflection.v1alpha.ExtensionRequest.containing_type)
 }
- void ExtensionRequest::set_containing_type(const char* value, size_t size) {
+void ExtensionRequest::set_containing_type(const char* value, size_t size) {
   
   containing_type_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
       ::std::string(reinterpret_cast<const char*>(value), size));
   // @@protoc_insertion_point(field_set_pointer:grpc.reflection.v1alpha.ExtensionRequest.containing_type)
 }
- ::std::string* ExtensionRequest::mutable_containing_type() {
+::std::string* ExtensionRequest::mutable_containing_type() {
   
   // @@protoc_insertion_point(field_mutable:grpc.reflection.v1alpha.ExtensionRequest.containing_type)
   return containing_type_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- ::std::string* ExtensionRequest::release_containing_type() {
+::std::string* ExtensionRequest::release_containing_type() {
   // @@protoc_insertion_point(field_release:grpc.reflection.v1alpha.ExtensionRequest.containing_type)
   
   return containing_type_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- void ExtensionRequest::set_allocated_containing_type(::std::string* containing_type) {
+void ExtensionRequest::set_allocated_containing_type(::std::string* containing_type) {
   if (containing_type != NULL) {
     
   } else {
@@ -1649,16 +1677,19 @@
 void ExtensionRequest::clear_extension_number() {
   extension_number_ = 0;
 }
- ::google::protobuf::int32 ExtensionRequest::extension_number() const {
+::google::protobuf::int32 ExtensionRequest::extension_number() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ExtensionRequest.extension_number)
   return extension_number_;
 }
- void ExtensionRequest::set_extension_number(::google::protobuf::int32 value) {
+void ExtensionRequest::set_extension_number(::google::protobuf::int32 value) {
   
   extension_number_ = value;
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ExtensionRequest.extension_number)
 }
 
+inline const ExtensionRequest* ExtensionRequest::internal_default_instance() {
+  return &ExtensionRequest_default_instance_.get();
+}
 #endif  // PROTOBUF_INLINE_NOT_IN_HEADERS
 
 // ===================================================================
@@ -1674,34 +1705,37 @@
 
 ServerReflectionResponse::ServerReflectionResponse()
   : ::google::protobuf::Message(), _internal_metadata_(NULL) {
+  if (this != internal_default_instance()) protobuf_InitDefaults_reflection_2eproto();
   SharedCtor();
   // @@protoc_insertion_point(constructor:grpc.reflection.v1alpha.ServerReflectionResponse)
 }
 
 void ServerReflectionResponse::InitAsDefaultInstance() {
-  _is_default_instance_ = true;
-  original_request_ = const_cast< ::grpc::reflection::v1alpha::ServerReflectionRequest*>(&::grpc::reflection::v1alpha::ServerReflectionRequest::default_instance());
-  ServerReflectionResponse_default_oneof_instance_->file_descriptor_response_ = const_cast< ::grpc::reflection::v1alpha::FileDescriptorResponse*>(&::grpc::reflection::v1alpha::FileDescriptorResponse::default_instance());
-  ServerReflectionResponse_default_oneof_instance_->all_extension_numbers_response_ = const_cast< ::grpc::reflection::v1alpha::ExtensionNumberResponse*>(&::grpc::reflection::v1alpha::ExtensionNumberResponse::default_instance());
-  ServerReflectionResponse_default_oneof_instance_->list_services_response_ = const_cast< ::grpc::reflection::v1alpha::ListServiceResponse*>(&::grpc::reflection::v1alpha::ListServiceResponse::default_instance());
-  ServerReflectionResponse_default_oneof_instance_->error_response_ = const_cast< ::grpc::reflection::v1alpha::ErrorResponse*>(&::grpc::reflection::v1alpha::ErrorResponse::default_instance());
+  original_request_ = const_cast< ::grpc::reflection::v1alpha::ServerReflectionRequest*>(
+      ::grpc::reflection::v1alpha::ServerReflectionRequest::internal_default_instance());
+  ServerReflectionResponse_default_oneof_instance_->file_descriptor_response_ = const_cast< ::grpc::reflection::v1alpha::FileDescriptorResponse*>(
+      ::grpc::reflection::v1alpha::FileDescriptorResponse::internal_default_instance());
+  ServerReflectionResponse_default_oneof_instance_->all_extension_numbers_response_ = const_cast< ::grpc::reflection::v1alpha::ExtensionNumberResponse*>(
+      ::grpc::reflection::v1alpha::ExtensionNumberResponse::internal_default_instance());
+  ServerReflectionResponse_default_oneof_instance_->list_services_response_ = const_cast< ::grpc::reflection::v1alpha::ListServiceResponse*>(
+      ::grpc::reflection::v1alpha::ListServiceResponse::internal_default_instance());
+  ServerReflectionResponse_default_oneof_instance_->error_response_ = const_cast< ::grpc::reflection::v1alpha::ErrorResponse*>(
+      ::grpc::reflection::v1alpha::ErrorResponse::internal_default_instance());
 }
 
 ServerReflectionResponse::ServerReflectionResponse(const ServerReflectionResponse& from)
   : ::google::protobuf::Message(),
     _internal_metadata_(NULL) {
   SharedCtor();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
   // @@protoc_insertion_point(copy_constructor:grpc.reflection.v1alpha.ServerReflectionResponse)
 }
 
 void ServerReflectionResponse::SharedCtor() {
-    _is_default_instance_ = false;
-  ::google::protobuf::internal::GetEmptyString();
-  _cached_size_ = 0;
   valid_host_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   original_request_ = NULL;
   clear_has_message_response();
+  _cached_size_ = 0;
 }
 
 ServerReflectionResponse::~ServerReflectionResponse() {
@@ -1714,7 +1748,7 @@
   if (has_message_response()) {
     clear_message_response();
   }
-  if (this != default_instance_) {
+  if (this != &ServerReflectionResponse_default_instance_.get()) {
     delete original_request_;
   }
 }
@@ -1730,11 +1764,11 @@
 }
 
 const ServerReflectionResponse& ServerReflectionResponse::default_instance() {
-  if (default_instance_ == NULL) protobuf_AddDesc_reflection_2eproto();
-  return *default_instance_;
+  protobuf_InitDefaults_reflection_2eproto();
+  return *internal_default_instance();
 }
 
-ServerReflectionResponse* ServerReflectionResponse::default_instance_ = NULL;
+::google::protobuf::internal::ExplicitlyConstructed<ServerReflectionResponse> ServerReflectionResponse_default_instance_;
 
 ServerReflectionResponse* ServerReflectionResponse::New(::google::protobuf::Arena* arena) const {
   ServerReflectionResponse* n = new ServerReflectionResponse;
@@ -1746,7 +1780,7 @@
 
 void ServerReflectionResponse::clear_message_response() {
 // @@protoc_insertion_point(one_of_clear_start:grpc.reflection.v1alpha.ServerReflectionResponse)
-  switch(message_response_case()) {
+  switch (message_response_case()) {
     case kFileDescriptorResponse: {
       delete message_response_.file_descriptor_response_;
       break;
@@ -1827,27 +1861,25 @@
         } else {
           goto handle_unusual;
         }
-        if (input->ExpectTag(42)) goto parse_all_extension_numbers_response;
+        goto after_error_response;
         break;
       }
 
       // optional .grpc.reflection.v1alpha.ExtensionNumberResponse all_extension_numbers_response = 5;
       case 5: {
         if (tag == 42) {
-         parse_all_extension_numbers_response:
           DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
                input, mutable_all_extension_numbers_response()));
         } else {
           goto handle_unusual;
         }
-        if (input->ExpectTag(50)) goto parse_list_services_response;
+        goto after_error_response;
         break;
       }
 
       // optional .grpc.reflection.v1alpha.ListServiceResponse list_services_response = 6;
       case 6: {
         if (tag == 50) {
-         parse_list_services_response:
           DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
                input, mutable_list_services_response()));
         } else {
@@ -1866,6 +1898,7 @@
         } else {
           goto handle_unusual;
         }
+       after_error_response:
         if (input->ExpectAtEnd()) goto success;
         break;
       }
@@ -1937,8 +1970,9 @@
   // @@protoc_insertion_point(serialize_end:grpc.reflection.v1alpha.ServerReflectionResponse)
 }
 
-::google::protobuf::uint8* ServerReflectionResponse::SerializeWithCachedSizesToArray(
-    ::google::protobuf::uint8* target) const {
+::google::protobuf::uint8* ServerReflectionResponse::InternalSerializeWithCachedSizesToArray(
+    bool deterministic, ::google::protobuf::uint8* target) const {
+  (void)deterministic; // Unused
   // @@protoc_insertion_point(serialize_to_array_start:grpc.reflection.v1alpha.ServerReflectionResponse)
   // optional string valid_host = 1;
   if (this->valid_host().size() > 0) {
@@ -1954,45 +1988,45 @@
   // optional .grpc.reflection.v1alpha.ServerReflectionRequest original_request = 2;
   if (this->has_original_request()) {
     target = ::google::protobuf::internal::WireFormatLite::
-      WriteMessageNoVirtualToArray(
-        2, *this->original_request_, target);
+      InternalWriteMessageNoVirtualToArray(
+        2, *this->original_request_, false, target);
   }
 
   // optional .grpc.reflection.v1alpha.FileDescriptorResponse file_descriptor_response = 4;
   if (has_file_descriptor_response()) {
     target = ::google::protobuf::internal::WireFormatLite::
-      WriteMessageNoVirtualToArray(
-        4, *message_response_.file_descriptor_response_, target);
+      InternalWriteMessageNoVirtualToArray(
+        4, *message_response_.file_descriptor_response_, false, target);
   }
 
   // optional .grpc.reflection.v1alpha.ExtensionNumberResponse all_extension_numbers_response = 5;
   if (has_all_extension_numbers_response()) {
     target = ::google::protobuf::internal::WireFormatLite::
-      WriteMessageNoVirtualToArray(
-        5, *message_response_.all_extension_numbers_response_, target);
+      InternalWriteMessageNoVirtualToArray(
+        5, *message_response_.all_extension_numbers_response_, false, target);
   }
 
   // optional .grpc.reflection.v1alpha.ListServiceResponse list_services_response = 6;
   if (has_list_services_response()) {
     target = ::google::protobuf::internal::WireFormatLite::
-      WriteMessageNoVirtualToArray(
-        6, *message_response_.list_services_response_, target);
+      InternalWriteMessageNoVirtualToArray(
+        6, *message_response_.list_services_response_, false, target);
   }
 
   // optional .grpc.reflection.v1alpha.ErrorResponse error_response = 7;
   if (has_error_response()) {
     target = ::google::protobuf::internal::WireFormatLite::
-      WriteMessageNoVirtualToArray(
-        7, *message_response_.error_response_, target);
+      InternalWriteMessageNoVirtualToArray(
+        7, *message_response_.error_response_, false, target);
   }
 
   // @@protoc_insertion_point(serialize_to_array_end:grpc.reflection.v1alpha.ServerReflectionResponse)
   return target;
 }
 
-int ServerReflectionResponse::ByteSize() const {
+size_t ServerReflectionResponse::ByteSizeLong() const {
 // @@protoc_insertion_point(message_byte_size_start:grpc.reflection.v1alpha.ServerReflectionResponse)
-  int total_size = 0;
+  size_t total_size = 0;
 
   // optional string valid_host = 1;
   if (this->valid_host().size() > 0) {
@@ -2041,8 +2075,9 @@
       break;
     }
   }
+  int cached_size = ::google::protobuf::internal::ToCachedSize(total_size);
   GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
-  _cached_size_ = total_size;
+  _cached_size_ = cached_size;
   GOOGLE_SAFE_CONCURRENT_WRITES_END();
   return total_size;
 }
@@ -2050,7 +2085,7 @@
 void ServerReflectionResponse::MergeFrom(const ::google::protobuf::Message& from) {
 // @@protoc_insertion_point(generalized_merge_from_start:grpc.reflection.v1alpha.ServerReflectionResponse)
   if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
-  const ServerReflectionResponse* source = 
+  const ServerReflectionResponse* source =
       ::google::protobuf::internal::DynamicCastToGenerated<const ServerReflectionResponse>(
           &from);
   if (source == NULL) {
@@ -2058,13 +2093,21 @@
     ::google::protobuf::internal::ReflectionOps::Merge(from, this);
   } else {
   // @@protoc_insertion_point(generalized_merge_from_cast_success:grpc.reflection.v1alpha.ServerReflectionResponse)
-    MergeFrom(*source);
+    UnsafeMergeFrom(*source);
   }
 }
 
 void ServerReflectionResponse::MergeFrom(const ServerReflectionResponse& from) {
 // @@protoc_insertion_point(class_specific_merge_from_start:grpc.reflection.v1alpha.ServerReflectionResponse)
-  if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+  if (GOOGLE_PREDICT_TRUE(&from != this)) {
+    UnsafeMergeFrom(from);
+  } else {
+    MergeFromFail(__LINE__);
+  }
+}
+
+void ServerReflectionResponse::UnsafeMergeFrom(const ServerReflectionResponse& from) {
+  GOOGLE_DCHECK(&from != this);
   switch (from.message_response_case()) {
     case kFileDescriptorResponse: {
       mutable_file_descriptor_response()->::grpc::reflection::v1alpha::FileDescriptorResponse::MergeFrom(from.file_descriptor_response());
@@ -2106,7 +2149,7 @@
 // @@protoc_insertion_point(class_specific_copy_from_start:grpc.reflection.v1alpha.ServerReflectionResponse)
   if (&from == this) return;
   Clear();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
 }
 
 bool ServerReflectionResponse::IsInitialized() const {
@@ -2142,37 +2185,37 @@
 void ServerReflectionResponse::clear_valid_host() {
   valid_host_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- const ::std::string& ServerReflectionResponse::valid_host() const {
+const ::std::string& ServerReflectionResponse::valid_host() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ServerReflectionResponse.valid_host)
   return valid_host_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- void ServerReflectionResponse::set_valid_host(const ::std::string& value) {
+void ServerReflectionResponse::set_valid_host(const ::std::string& value) {
   
   valid_host_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ServerReflectionResponse.valid_host)
 }
- void ServerReflectionResponse::set_valid_host(const char* value) {
+void ServerReflectionResponse::set_valid_host(const char* value) {
   
   valid_host_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
   // @@protoc_insertion_point(field_set_char:grpc.reflection.v1alpha.ServerReflectionResponse.valid_host)
 }
- void ServerReflectionResponse::set_valid_host(const char* value, size_t size) {
+void ServerReflectionResponse::set_valid_host(const char* value, size_t size) {
   
   valid_host_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
       ::std::string(reinterpret_cast<const char*>(value), size));
   // @@protoc_insertion_point(field_set_pointer:grpc.reflection.v1alpha.ServerReflectionResponse.valid_host)
 }
- ::std::string* ServerReflectionResponse::mutable_valid_host() {
+::std::string* ServerReflectionResponse::mutable_valid_host() {
   
   // @@protoc_insertion_point(field_mutable:grpc.reflection.v1alpha.ServerReflectionResponse.valid_host)
   return valid_host_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- ::std::string* ServerReflectionResponse::release_valid_host() {
+::std::string* ServerReflectionResponse::release_valid_host() {
   // @@protoc_insertion_point(field_release:grpc.reflection.v1alpha.ServerReflectionResponse.valid_host)
   
   return valid_host_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- void ServerReflectionResponse::set_allocated_valid_host(::std::string* valid_host) {
+void ServerReflectionResponse::set_allocated_valid_host(::std::string* valid_host) {
   if (valid_host != NULL) {
     
   } else {
@@ -2184,7 +2227,7 @@
 
 // optional .grpc.reflection.v1alpha.ServerReflectionRequest original_request = 2;
 bool ServerReflectionResponse::has_original_request() const {
-  return !_is_default_instance_ && original_request_ != NULL;
+  return this != internal_default_instance() && original_request_ != NULL;
 }
 void ServerReflectionResponse::clear_original_request() {
   if (GetArenaNoVirtual() == NULL && original_request_ != NULL) delete original_request_;
@@ -2192,7 +2235,8 @@
 }
 const ::grpc::reflection::v1alpha::ServerReflectionRequest& ServerReflectionResponse::original_request() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ServerReflectionResponse.original_request)
-  return original_request_ != NULL ? *original_request_ : *default_instance_->original_request_;
+  return original_request_ != NULL ? *original_request_
+                         : *::grpc::reflection::v1alpha::ServerReflectionRequest::internal_default_instance();
 }
 ::grpc::reflection::v1alpha::ServerReflectionRequest* ServerReflectionResponse::mutable_original_request() {
   
@@ -2421,6 +2465,9 @@
 ServerReflectionResponse::MessageResponseCase ServerReflectionResponse::message_response_case() const {
   return ServerReflectionResponse::MessageResponseCase(_oneof_case_[0]);
 }
+inline const ServerReflectionResponse* ServerReflectionResponse::internal_default_instance() {
+  return &ServerReflectionResponse_default_instance_.get();
+}
 #endif  // PROTOBUF_INLINE_NOT_IN_HEADERS
 
 // ===================================================================
@@ -2431,25 +2478,23 @@
 
 FileDescriptorResponse::FileDescriptorResponse()
   : ::google::protobuf::Message(), _internal_metadata_(NULL) {
+  if (this != internal_default_instance()) protobuf_InitDefaults_reflection_2eproto();
   SharedCtor();
   // @@protoc_insertion_point(constructor:grpc.reflection.v1alpha.FileDescriptorResponse)
 }
 
 void FileDescriptorResponse::InitAsDefaultInstance() {
-  _is_default_instance_ = true;
 }
 
 FileDescriptorResponse::FileDescriptorResponse(const FileDescriptorResponse& from)
   : ::google::protobuf::Message(),
     _internal_metadata_(NULL) {
   SharedCtor();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
   // @@protoc_insertion_point(copy_constructor:grpc.reflection.v1alpha.FileDescriptorResponse)
 }
 
 void FileDescriptorResponse::SharedCtor() {
-    _is_default_instance_ = false;
-  ::google::protobuf::internal::GetEmptyString();
   _cached_size_ = 0;
 }
 
@@ -2459,8 +2504,6 @@
 }
 
 void FileDescriptorResponse::SharedDtor() {
-  if (this != default_instance_) {
-  }
 }
 
 void FileDescriptorResponse::SetCachedSize(int size) const {
@@ -2474,11 +2517,11 @@
 }
 
 const FileDescriptorResponse& FileDescriptorResponse::default_instance() {
-  if (default_instance_ == NULL) protobuf_AddDesc_reflection_2eproto();
-  return *default_instance_;
+  protobuf_InitDefaults_reflection_2eproto();
+  return *internal_default_instance();
 }
 
-FileDescriptorResponse* FileDescriptorResponse::default_instance_ = NULL;
+::google::protobuf::internal::ExplicitlyConstructed<FileDescriptorResponse> FileDescriptorResponse_default_instance_;
 
 FileDescriptorResponse* FileDescriptorResponse::New(::google::protobuf::Arena* arena) const {
   FileDescriptorResponse* n = new FileDescriptorResponse;
@@ -2550,8 +2593,9 @@
   // @@protoc_insertion_point(serialize_end:grpc.reflection.v1alpha.FileDescriptorResponse)
 }
 
-::google::protobuf::uint8* FileDescriptorResponse::SerializeWithCachedSizesToArray(
-    ::google::protobuf::uint8* target) const {
+::google::protobuf::uint8* FileDescriptorResponse::InternalSerializeWithCachedSizesToArray(
+    bool deterministic, ::google::protobuf::uint8* target) const {
+  (void)deterministic; // Unused
   // @@protoc_insertion_point(serialize_to_array_start:grpc.reflection.v1alpha.FileDescriptorResponse)
   // repeated bytes file_descriptor_proto = 1;
   for (int i = 0; i < this->file_descriptor_proto_size(); i++) {
@@ -2563,19 +2607,21 @@
   return target;
 }
 
-int FileDescriptorResponse::ByteSize() const {
+size_t FileDescriptorResponse::ByteSizeLong() const {
 // @@protoc_insertion_point(message_byte_size_start:grpc.reflection.v1alpha.FileDescriptorResponse)
-  int total_size = 0;
+  size_t total_size = 0;
 
   // repeated bytes file_descriptor_proto = 1;
-  total_size += 1 * this->file_descriptor_proto_size();
+  total_size += 1 *
+      ::google::protobuf::internal::FromIntSize(this->file_descriptor_proto_size());
   for (int i = 0; i < this->file_descriptor_proto_size(); i++) {
     total_size += ::google::protobuf::internal::WireFormatLite::BytesSize(
       this->file_descriptor_proto(i));
   }
 
+  int cached_size = ::google::protobuf::internal::ToCachedSize(total_size);
   GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
-  _cached_size_ = total_size;
+  _cached_size_ = cached_size;
   GOOGLE_SAFE_CONCURRENT_WRITES_END();
   return total_size;
 }
@@ -2583,7 +2629,7 @@
 void FileDescriptorResponse::MergeFrom(const ::google::protobuf::Message& from) {
 // @@protoc_insertion_point(generalized_merge_from_start:grpc.reflection.v1alpha.FileDescriptorResponse)
   if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
-  const FileDescriptorResponse* source = 
+  const FileDescriptorResponse* source =
       ::google::protobuf::internal::DynamicCastToGenerated<const FileDescriptorResponse>(
           &from);
   if (source == NULL) {
@@ -2591,14 +2637,22 @@
     ::google::protobuf::internal::ReflectionOps::Merge(from, this);
   } else {
   // @@protoc_insertion_point(generalized_merge_from_cast_success:grpc.reflection.v1alpha.FileDescriptorResponse)
-    MergeFrom(*source);
+    UnsafeMergeFrom(*source);
   }
 }
 
 void FileDescriptorResponse::MergeFrom(const FileDescriptorResponse& from) {
 // @@protoc_insertion_point(class_specific_merge_from_start:grpc.reflection.v1alpha.FileDescriptorResponse)
-  if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
-  file_descriptor_proto_.MergeFrom(from.file_descriptor_proto_);
+  if (GOOGLE_PREDICT_TRUE(&from != this)) {
+    UnsafeMergeFrom(from);
+  } else {
+    MergeFromFail(__LINE__);
+  }
+}
+
+void FileDescriptorResponse::UnsafeMergeFrom(const FileDescriptorResponse& from) {
+  GOOGLE_DCHECK(&from != this);
+  file_descriptor_proto_.UnsafeMergeFrom(from.file_descriptor_proto_);
 }
 
 void FileDescriptorResponse::CopyFrom(const ::google::protobuf::Message& from) {
@@ -2612,7 +2666,7 @@
 // @@protoc_insertion_point(class_specific_copy_from_start:grpc.reflection.v1alpha.FileDescriptorResponse)
   if (&from == this) return;
   Clear();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
 }
 
 bool FileDescriptorResponse::IsInitialized() const {
@@ -2648,54 +2702,57 @@
 void FileDescriptorResponse::clear_file_descriptor_proto() {
   file_descriptor_proto_.Clear();
 }
- const ::std::string& FileDescriptorResponse::file_descriptor_proto(int index) const {
+const ::std::string& FileDescriptorResponse::file_descriptor_proto(int index) const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.FileDescriptorResponse.file_descriptor_proto)
   return file_descriptor_proto_.Get(index);
 }
- ::std::string* FileDescriptorResponse::mutable_file_descriptor_proto(int index) {
+::std::string* FileDescriptorResponse::mutable_file_descriptor_proto(int index) {
   // @@protoc_insertion_point(field_mutable:grpc.reflection.v1alpha.FileDescriptorResponse.file_descriptor_proto)
   return file_descriptor_proto_.Mutable(index);
 }
- void FileDescriptorResponse::set_file_descriptor_proto(int index, const ::std::string& value) {
+void FileDescriptorResponse::set_file_descriptor_proto(int index, const ::std::string& value) {
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.FileDescriptorResponse.file_descriptor_proto)
   file_descriptor_proto_.Mutable(index)->assign(value);
 }
- void FileDescriptorResponse::set_file_descriptor_proto(int index, const char* value) {
+void FileDescriptorResponse::set_file_descriptor_proto(int index, const char* value) {
   file_descriptor_proto_.Mutable(index)->assign(value);
   // @@protoc_insertion_point(field_set_char:grpc.reflection.v1alpha.FileDescriptorResponse.file_descriptor_proto)
 }
- void FileDescriptorResponse::set_file_descriptor_proto(int index, const void* value, size_t size) {
+void FileDescriptorResponse::set_file_descriptor_proto(int index, const void* value, size_t size) {
   file_descriptor_proto_.Mutable(index)->assign(
     reinterpret_cast<const char*>(value), size);
   // @@protoc_insertion_point(field_set_pointer:grpc.reflection.v1alpha.FileDescriptorResponse.file_descriptor_proto)
 }
- ::std::string* FileDescriptorResponse::add_file_descriptor_proto() {
+::std::string* FileDescriptorResponse::add_file_descriptor_proto() {
   // @@protoc_insertion_point(field_add_mutable:grpc.reflection.v1alpha.FileDescriptorResponse.file_descriptor_proto)
   return file_descriptor_proto_.Add();
 }
- void FileDescriptorResponse::add_file_descriptor_proto(const ::std::string& value) {
+void FileDescriptorResponse::add_file_descriptor_proto(const ::std::string& value) {
   file_descriptor_proto_.Add()->assign(value);
   // @@protoc_insertion_point(field_add:grpc.reflection.v1alpha.FileDescriptorResponse.file_descriptor_proto)
 }
- void FileDescriptorResponse::add_file_descriptor_proto(const char* value) {
+void FileDescriptorResponse::add_file_descriptor_proto(const char* value) {
   file_descriptor_proto_.Add()->assign(value);
   // @@protoc_insertion_point(field_add_char:grpc.reflection.v1alpha.FileDescriptorResponse.file_descriptor_proto)
 }
- void FileDescriptorResponse::add_file_descriptor_proto(const void* value, size_t size) {
+void FileDescriptorResponse::add_file_descriptor_proto(const void* value, size_t size) {
   file_descriptor_proto_.Add()->assign(reinterpret_cast<const char*>(value), size);
   // @@protoc_insertion_point(field_add_pointer:grpc.reflection.v1alpha.FileDescriptorResponse.file_descriptor_proto)
 }
- const ::google::protobuf::RepeatedPtrField< ::std::string>&
+const ::google::protobuf::RepeatedPtrField< ::std::string>&
 FileDescriptorResponse::file_descriptor_proto() const {
   // @@protoc_insertion_point(field_list:grpc.reflection.v1alpha.FileDescriptorResponse.file_descriptor_proto)
   return file_descriptor_proto_;
 }
- ::google::protobuf::RepeatedPtrField< ::std::string>*
+::google::protobuf::RepeatedPtrField< ::std::string>*
 FileDescriptorResponse::mutable_file_descriptor_proto() {
   // @@protoc_insertion_point(field_mutable_list:grpc.reflection.v1alpha.FileDescriptorResponse.file_descriptor_proto)
   return &file_descriptor_proto_;
 }
 
+inline const FileDescriptorResponse* FileDescriptorResponse::internal_default_instance() {
+  return &FileDescriptorResponse_default_instance_.get();
+}
 #endif  // PROTOBUF_INLINE_NOT_IN_HEADERS
 
 // ===================================================================
@@ -2707,27 +2764,25 @@
 
 ExtensionNumberResponse::ExtensionNumberResponse()
   : ::google::protobuf::Message(), _internal_metadata_(NULL) {
+  if (this != internal_default_instance()) protobuf_InitDefaults_reflection_2eproto();
   SharedCtor();
   // @@protoc_insertion_point(constructor:grpc.reflection.v1alpha.ExtensionNumberResponse)
 }
 
 void ExtensionNumberResponse::InitAsDefaultInstance() {
-  _is_default_instance_ = true;
 }
 
 ExtensionNumberResponse::ExtensionNumberResponse(const ExtensionNumberResponse& from)
   : ::google::protobuf::Message(),
     _internal_metadata_(NULL) {
   SharedCtor();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
   // @@protoc_insertion_point(copy_constructor:grpc.reflection.v1alpha.ExtensionNumberResponse)
 }
 
 void ExtensionNumberResponse::SharedCtor() {
-    _is_default_instance_ = false;
-  ::google::protobuf::internal::GetEmptyString();
-  _cached_size_ = 0;
   base_type_name_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+  _cached_size_ = 0;
 }
 
 ExtensionNumberResponse::~ExtensionNumberResponse() {
@@ -2737,8 +2792,6 @@
 
 void ExtensionNumberResponse::SharedDtor() {
   base_type_name_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
-  if (this != default_instance_) {
-  }
 }
 
 void ExtensionNumberResponse::SetCachedSize(int size) const {
@@ -2752,11 +2805,11 @@
 }
 
 const ExtensionNumberResponse& ExtensionNumberResponse::default_instance() {
-  if (default_instance_ == NULL) protobuf_AddDesc_reflection_2eproto();
-  return *default_instance_;
+  protobuf_InitDefaults_reflection_2eproto();
+  return *internal_default_instance();
 }
 
-ExtensionNumberResponse* ExtensionNumberResponse::default_instance_ = NULL;
+::google::protobuf::internal::ExplicitlyConstructed<ExtensionNumberResponse> ExtensionNumberResponse_default_instance_;
 
 ExtensionNumberResponse* ExtensionNumberResponse::New(::google::protobuf::Arena* arena) const {
   ExtensionNumberResponse* n = new ExtensionNumberResponse;
@@ -2863,8 +2916,9 @@
   // @@protoc_insertion_point(serialize_end:grpc.reflection.v1alpha.ExtensionNumberResponse)
 }
 
-::google::protobuf::uint8* ExtensionNumberResponse::SerializeWithCachedSizesToArray(
-    ::google::protobuf::uint8* target) const {
+::google::protobuf::uint8* ExtensionNumberResponse::InternalSerializeWithCachedSizesToArray(
+    bool deterministic, ::google::protobuf::uint8* target) const {
+  (void)deterministic; // Unused
   // @@protoc_insertion_point(serialize_to_array_start:grpc.reflection.v1alpha.ExtensionNumberResponse)
   // optional string base_type_name = 1;
   if (this->base_type_name().size() > 0) {
@@ -2895,9 +2949,9 @@
   return target;
 }
 
-int ExtensionNumberResponse::ByteSize() const {
+size_t ExtensionNumberResponse::ByteSizeLong() const {
 // @@protoc_insertion_point(message_byte_size_start:grpc.reflection.v1alpha.ExtensionNumberResponse)
-  int total_size = 0;
+  size_t total_size = 0;
 
   // optional string base_type_name = 1;
   if (this->base_type_name().size() > 0) {
@@ -2908,8 +2962,9 @@
 
   // repeated int32 extension_number = 2;
   {
-    int data_size = 0;
-    for (int i = 0; i < this->extension_number_size(); i++) {
+    size_t data_size = 0;
+    unsigned int count = this->extension_number_size();
+    for (unsigned int i = 0; i < count; i++) {
       data_size += ::google::protobuf::internal::WireFormatLite::
         Int32Size(this->extension_number(i));
     }
@@ -2917,14 +2972,16 @@
       total_size += 1 +
         ::google::protobuf::internal::WireFormatLite::Int32Size(data_size);
     }
+    int cached_size = ::google::protobuf::internal::ToCachedSize(data_size);
     GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
-    _extension_number_cached_byte_size_ = data_size;
+    _extension_number_cached_byte_size_ = cached_size;
     GOOGLE_SAFE_CONCURRENT_WRITES_END();
     total_size += data_size;
   }
 
+  int cached_size = ::google::protobuf::internal::ToCachedSize(total_size);
   GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
-  _cached_size_ = total_size;
+  _cached_size_ = cached_size;
   GOOGLE_SAFE_CONCURRENT_WRITES_END();
   return total_size;
 }
@@ -2932,7 +2989,7 @@
 void ExtensionNumberResponse::MergeFrom(const ::google::protobuf::Message& from) {
 // @@protoc_insertion_point(generalized_merge_from_start:grpc.reflection.v1alpha.ExtensionNumberResponse)
   if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
-  const ExtensionNumberResponse* source = 
+  const ExtensionNumberResponse* source =
       ::google::protobuf::internal::DynamicCastToGenerated<const ExtensionNumberResponse>(
           &from);
   if (source == NULL) {
@@ -2940,14 +2997,22 @@
     ::google::protobuf::internal::ReflectionOps::Merge(from, this);
   } else {
   // @@protoc_insertion_point(generalized_merge_from_cast_success:grpc.reflection.v1alpha.ExtensionNumberResponse)
-    MergeFrom(*source);
+    UnsafeMergeFrom(*source);
   }
 }
 
 void ExtensionNumberResponse::MergeFrom(const ExtensionNumberResponse& from) {
 // @@protoc_insertion_point(class_specific_merge_from_start:grpc.reflection.v1alpha.ExtensionNumberResponse)
-  if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
-  extension_number_.MergeFrom(from.extension_number_);
+  if (GOOGLE_PREDICT_TRUE(&from != this)) {
+    UnsafeMergeFrom(from);
+  } else {
+    MergeFromFail(__LINE__);
+  }
+}
+
+void ExtensionNumberResponse::UnsafeMergeFrom(const ExtensionNumberResponse& from) {
+  GOOGLE_DCHECK(&from != this);
+  extension_number_.UnsafeMergeFrom(from.extension_number_);
   if (from.base_type_name().size() > 0) {
 
     base_type_name_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.base_type_name_);
@@ -2965,7 +3030,7 @@
 // @@protoc_insertion_point(class_specific_copy_from_start:grpc.reflection.v1alpha.ExtensionNumberResponse)
   if (&from == this) return;
   Clear();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
 }
 
 bool ExtensionNumberResponse::IsInitialized() const {
@@ -2999,37 +3064,37 @@
 void ExtensionNumberResponse::clear_base_type_name() {
   base_type_name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- const ::std::string& ExtensionNumberResponse::base_type_name() const {
+const ::std::string& ExtensionNumberResponse::base_type_name() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ExtensionNumberResponse.base_type_name)
   return base_type_name_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- void ExtensionNumberResponse::set_base_type_name(const ::std::string& value) {
+void ExtensionNumberResponse::set_base_type_name(const ::std::string& value) {
   
   base_type_name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ExtensionNumberResponse.base_type_name)
 }
- void ExtensionNumberResponse::set_base_type_name(const char* value) {
+void ExtensionNumberResponse::set_base_type_name(const char* value) {
   
   base_type_name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
   // @@protoc_insertion_point(field_set_char:grpc.reflection.v1alpha.ExtensionNumberResponse.base_type_name)
 }
- void ExtensionNumberResponse::set_base_type_name(const char* value, size_t size) {
+void ExtensionNumberResponse::set_base_type_name(const char* value, size_t size) {
   
   base_type_name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
       ::std::string(reinterpret_cast<const char*>(value), size));
   // @@protoc_insertion_point(field_set_pointer:grpc.reflection.v1alpha.ExtensionNumberResponse.base_type_name)
 }
- ::std::string* ExtensionNumberResponse::mutable_base_type_name() {
+::std::string* ExtensionNumberResponse::mutable_base_type_name() {
   
   // @@protoc_insertion_point(field_mutable:grpc.reflection.v1alpha.ExtensionNumberResponse.base_type_name)
   return base_type_name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- ::std::string* ExtensionNumberResponse::release_base_type_name() {
+::std::string* ExtensionNumberResponse::release_base_type_name() {
   // @@protoc_insertion_point(field_release:grpc.reflection.v1alpha.ExtensionNumberResponse.base_type_name)
   
   return base_type_name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- void ExtensionNumberResponse::set_allocated_base_type_name(::std::string* base_type_name) {
+void ExtensionNumberResponse::set_allocated_base_type_name(::std::string* base_type_name) {
   if (base_type_name != NULL) {
     
   } else {
@@ -3046,29 +3111,32 @@
 void ExtensionNumberResponse::clear_extension_number() {
   extension_number_.Clear();
 }
- ::google::protobuf::int32 ExtensionNumberResponse::extension_number(int index) const {
+::google::protobuf::int32 ExtensionNumberResponse::extension_number(int index) const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ExtensionNumberResponse.extension_number)
   return extension_number_.Get(index);
 }
- void ExtensionNumberResponse::set_extension_number(int index, ::google::protobuf::int32 value) {
+void ExtensionNumberResponse::set_extension_number(int index, ::google::protobuf::int32 value) {
   extension_number_.Set(index, value);
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ExtensionNumberResponse.extension_number)
 }
- void ExtensionNumberResponse::add_extension_number(::google::protobuf::int32 value) {
+void ExtensionNumberResponse::add_extension_number(::google::protobuf::int32 value) {
   extension_number_.Add(value);
   // @@protoc_insertion_point(field_add:grpc.reflection.v1alpha.ExtensionNumberResponse.extension_number)
 }
- const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
+const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
 ExtensionNumberResponse::extension_number() const {
   // @@protoc_insertion_point(field_list:grpc.reflection.v1alpha.ExtensionNumberResponse.extension_number)
   return extension_number_;
 }
- ::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
+::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
 ExtensionNumberResponse::mutable_extension_number() {
   // @@protoc_insertion_point(field_mutable_list:grpc.reflection.v1alpha.ExtensionNumberResponse.extension_number)
   return &extension_number_;
 }
 
+inline const ExtensionNumberResponse* ExtensionNumberResponse::internal_default_instance() {
+  return &ExtensionNumberResponse_default_instance_.get();
+}
 #endif  // PROTOBUF_INLINE_NOT_IN_HEADERS
 
 // ===================================================================
@@ -3079,24 +3147,23 @@
 
 ListServiceResponse::ListServiceResponse()
   : ::google::protobuf::Message(), _internal_metadata_(NULL) {
+  if (this != internal_default_instance()) protobuf_InitDefaults_reflection_2eproto();
   SharedCtor();
   // @@protoc_insertion_point(constructor:grpc.reflection.v1alpha.ListServiceResponse)
 }
 
 void ListServiceResponse::InitAsDefaultInstance() {
-  _is_default_instance_ = true;
 }
 
 ListServiceResponse::ListServiceResponse(const ListServiceResponse& from)
   : ::google::protobuf::Message(),
     _internal_metadata_(NULL) {
   SharedCtor();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
   // @@protoc_insertion_point(copy_constructor:grpc.reflection.v1alpha.ListServiceResponse)
 }
 
 void ListServiceResponse::SharedCtor() {
-    _is_default_instance_ = false;
   _cached_size_ = 0;
 }
 
@@ -3106,8 +3173,6 @@
 }
 
 void ListServiceResponse::SharedDtor() {
-  if (this != default_instance_) {
-  }
 }
 
 void ListServiceResponse::SetCachedSize(int size) const {
@@ -3121,11 +3186,11 @@
 }
 
 const ListServiceResponse& ListServiceResponse::default_instance() {
-  if (default_instance_ == NULL) protobuf_AddDesc_reflection_2eproto();
-  return *default_instance_;
+  protobuf_InitDefaults_reflection_2eproto();
+  return *internal_default_instance();
 }
 
-ListServiceResponse* ListServiceResponse::default_instance_ = NULL;
+::google::protobuf::internal::ExplicitlyConstructed<ListServiceResponse> ListServiceResponse_default_instance_;
 
 ListServiceResponse* ListServiceResponse::New(::google::protobuf::Arena* arena) const {
   ListServiceResponse* n = new ListServiceResponse;
@@ -3199,34 +3264,39 @@
   // @@protoc_insertion_point(serialize_end:grpc.reflection.v1alpha.ListServiceResponse)
 }
 
-::google::protobuf::uint8* ListServiceResponse::SerializeWithCachedSizesToArray(
-    ::google::protobuf::uint8* target) const {
+::google::protobuf::uint8* ListServiceResponse::InternalSerializeWithCachedSizesToArray(
+    bool deterministic, ::google::protobuf::uint8* target) const {
+  (void)deterministic; // Unused
   // @@protoc_insertion_point(serialize_to_array_start:grpc.reflection.v1alpha.ListServiceResponse)
   // repeated .grpc.reflection.v1alpha.ServiceResponse service = 1;
   for (unsigned int i = 0, n = this->service_size(); i < n; i++) {
     target = ::google::protobuf::internal::WireFormatLite::
-      WriteMessageNoVirtualToArray(
-        1, this->service(i), target);
+      InternalWriteMessageNoVirtualToArray(
+        1, this->service(i), false, target);
   }
 
   // @@protoc_insertion_point(serialize_to_array_end:grpc.reflection.v1alpha.ListServiceResponse)
   return target;
 }
 
-int ListServiceResponse::ByteSize() const {
+size_t ListServiceResponse::ByteSizeLong() const {
 // @@protoc_insertion_point(message_byte_size_start:grpc.reflection.v1alpha.ListServiceResponse)
-  int total_size = 0;
+  size_t total_size = 0;
 
   // repeated .grpc.reflection.v1alpha.ServiceResponse service = 1;
-  total_size += 1 * this->service_size();
-  for (int i = 0; i < this->service_size(); i++) {
-    total_size +=
-      ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
-        this->service(i));
+  {
+    unsigned int count = this->service_size();
+    total_size += 1UL * count;
+    for (unsigned int i = 0; i < count; i++) {
+      total_size +=
+        ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+          this->service(i));
+    }
   }
 
+  int cached_size = ::google::protobuf::internal::ToCachedSize(total_size);
   GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
-  _cached_size_ = total_size;
+  _cached_size_ = cached_size;
   GOOGLE_SAFE_CONCURRENT_WRITES_END();
   return total_size;
 }
@@ -3234,7 +3304,7 @@
 void ListServiceResponse::MergeFrom(const ::google::protobuf::Message& from) {
 // @@protoc_insertion_point(generalized_merge_from_start:grpc.reflection.v1alpha.ListServiceResponse)
   if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
-  const ListServiceResponse* source = 
+  const ListServiceResponse* source =
       ::google::protobuf::internal::DynamicCastToGenerated<const ListServiceResponse>(
           &from);
   if (source == NULL) {
@@ -3242,13 +3312,21 @@
     ::google::protobuf::internal::ReflectionOps::Merge(from, this);
   } else {
   // @@protoc_insertion_point(generalized_merge_from_cast_success:grpc.reflection.v1alpha.ListServiceResponse)
-    MergeFrom(*source);
+    UnsafeMergeFrom(*source);
   }
 }
 
 void ListServiceResponse::MergeFrom(const ListServiceResponse& from) {
 // @@protoc_insertion_point(class_specific_merge_from_start:grpc.reflection.v1alpha.ListServiceResponse)
-  if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+  if (GOOGLE_PREDICT_TRUE(&from != this)) {
+    UnsafeMergeFrom(from);
+  } else {
+    MergeFromFail(__LINE__);
+  }
+}
+
+void ListServiceResponse::UnsafeMergeFrom(const ListServiceResponse& from) {
+  GOOGLE_DCHECK(&from != this);
   service_.MergeFrom(from.service_);
 }
 
@@ -3263,7 +3341,7 @@
 // @@protoc_insertion_point(class_specific_copy_from_start:grpc.reflection.v1alpha.ListServiceResponse)
   if (&from == this) return;
   Clear();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
 }
 
 bool ListServiceResponse::IsInitialized() const {
@@ -3322,6 +3400,9 @@
   return service_;
 }
 
+inline const ListServiceResponse* ListServiceResponse::internal_default_instance() {
+  return &ListServiceResponse_default_instance_.get();
+}
 #endif  // PROTOBUF_INLINE_NOT_IN_HEADERS
 
 // ===================================================================
@@ -3332,27 +3413,25 @@
 
 ServiceResponse::ServiceResponse()
   : ::google::protobuf::Message(), _internal_metadata_(NULL) {
+  if (this != internal_default_instance()) protobuf_InitDefaults_reflection_2eproto();
   SharedCtor();
   // @@protoc_insertion_point(constructor:grpc.reflection.v1alpha.ServiceResponse)
 }
 
 void ServiceResponse::InitAsDefaultInstance() {
-  _is_default_instance_ = true;
 }
 
 ServiceResponse::ServiceResponse(const ServiceResponse& from)
   : ::google::protobuf::Message(),
     _internal_metadata_(NULL) {
   SharedCtor();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
   // @@protoc_insertion_point(copy_constructor:grpc.reflection.v1alpha.ServiceResponse)
 }
 
 void ServiceResponse::SharedCtor() {
-    _is_default_instance_ = false;
-  ::google::protobuf::internal::GetEmptyString();
-  _cached_size_ = 0;
   name_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+  _cached_size_ = 0;
 }
 
 ServiceResponse::~ServiceResponse() {
@@ -3362,8 +3441,6 @@
 
 void ServiceResponse::SharedDtor() {
   name_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
-  if (this != default_instance_) {
-  }
 }
 
 void ServiceResponse::SetCachedSize(int size) const {
@@ -3377,11 +3454,11 @@
 }
 
 const ServiceResponse& ServiceResponse::default_instance() {
-  if (default_instance_ == NULL) protobuf_AddDesc_reflection_2eproto();
-  return *default_instance_;
+  protobuf_InitDefaults_reflection_2eproto();
+  return *internal_default_instance();
 }
 
-ServiceResponse* ServiceResponse::default_instance_ = NULL;
+::google::protobuf::internal::ExplicitlyConstructed<ServiceResponse> ServiceResponse_default_instance_;
 
 ServiceResponse* ServiceResponse::New(::google::protobuf::Arena* arena) const {
   ServiceResponse* n = new ServiceResponse;
@@ -3459,8 +3536,9 @@
   // @@protoc_insertion_point(serialize_end:grpc.reflection.v1alpha.ServiceResponse)
 }
 
-::google::protobuf::uint8* ServiceResponse::SerializeWithCachedSizesToArray(
-    ::google::protobuf::uint8* target) const {
+::google::protobuf::uint8* ServiceResponse::InternalSerializeWithCachedSizesToArray(
+    bool deterministic, ::google::protobuf::uint8* target) const {
+  (void)deterministic; // Unused
   // @@protoc_insertion_point(serialize_to_array_start:grpc.reflection.v1alpha.ServiceResponse)
   // optional string name = 1;
   if (this->name().size() > 0) {
@@ -3477,9 +3555,9 @@
   return target;
 }
 
-int ServiceResponse::ByteSize() const {
+size_t ServiceResponse::ByteSizeLong() const {
 // @@protoc_insertion_point(message_byte_size_start:grpc.reflection.v1alpha.ServiceResponse)
-  int total_size = 0;
+  size_t total_size = 0;
 
   // optional string name = 1;
   if (this->name().size() > 0) {
@@ -3488,8 +3566,9 @@
         this->name());
   }
 
+  int cached_size = ::google::protobuf::internal::ToCachedSize(total_size);
   GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
-  _cached_size_ = total_size;
+  _cached_size_ = cached_size;
   GOOGLE_SAFE_CONCURRENT_WRITES_END();
   return total_size;
 }
@@ -3497,7 +3576,7 @@
 void ServiceResponse::MergeFrom(const ::google::protobuf::Message& from) {
 // @@protoc_insertion_point(generalized_merge_from_start:grpc.reflection.v1alpha.ServiceResponse)
   if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
-  const ServiceResponse* source = 
+  const ServiceResponse* source =
       ::google::protobuf::internal::DynamicCastToGenerated<const ServiceResponse>(
           &from);
   if (source == NULL) {
@@ -3505,13 +3584,21 @@
     ::google::protobuf::internal::ReflectionOps::Merge(from, this);
   } else {
   // @@protoc_insertion_point(generalized_merge_from_cast_success:grpc.reflection.v1alpha.ServiceResponse)
-    MergeFrom(*source);
+    UnsafeMergeFrom(*source);
   }
 }
 
 void ServiceResponse::MergeFrom(const ServiceResponse& from) {
 // @@protoc_insertion_point(class_specific_merge_from_start:grpc.reflection.v1alpha.ServiceResponse)
-  if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+  if (GOOGLE_PREDICT_TRUE(&from != this)) {
+    UnsafeMergeFrom(from);
+  } else {
+    MergeFromFail(__LINE__);
+  }
+}
+
+void ServiceResponse::UnsafeMergeFrom(const ServiceResponse& from) {
+  GOOGLE_DCHECK(&from != this);
   if (from.name().size() > 0) {
 
     name_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.name_);
@@ -3529,7 +3616,7 @@
 // @@protoc_insertion_point(class_specific_copy_from_start:grpc.reflection.v1alpha.ServiceResponse)
   if (&from == this) return;
   Clear();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
 }
 
 bool ServiceResponse::IsInitialized() const {
@@ -3562,37 +3649,37 @@
 void ServiceResponse::clear_name() {
   name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- const ::std::string& ServiceResponse::name() const {
+const ::std::string& ServiceResponse::name() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ServiceResponse.name)
   return name_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- void ServiceResponse::set_name(const ::std::string& value) {
+void ServiceResponse::set_name(const ::std::string& value) {
   
   name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ServiceResponse.name)
 }
- void ServiceResponse::set_name(const char* value) {
+void ServiceResponse::set_name(const char* value) {
   
   name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
   // @@protoc_insertion_point(field_set_char:grpc.reflection.v1alpha.ServiceResponse.name)
 }
- void ServiceResponse::set_name(const char* value, size_t size) {
+void ServiceResponse::set_name(const char* value, size_t size) {
   
   name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
       ::std::string(reinterpret_cast<const char*>(value), size));
   // @@protoc_insertion_point(field_set_pointer:grpc.reflection.v1alpha.ServiceResponse.name)
 }
- ::std::string* ServiceResponse::mutable_name() {
+::std::string* ServiceResponse::mutable_name() {
   
   // @@protoc_insertion_point(field_mutable:grpc.reflection.v1alpha.ServiceResponse.name)
   return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- ::std::string* ServiceResponse::release_name() {
+::std::string* ServiceResponse::release_name() {
   // @@protoc_insertion_point(field_release:grpc.reflection.v1alpha.ServiceResponse.name)
   
   return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- void ServiceResponse::set_allocated_name(::std::string* name) {
+void ServiceResponse::set_allocated_name(::std::string* name) {
   if (name != NULL) {
     
   } else {
@@ -3602,6 +3689,9 @@
   // @@protoc_insertion_point(field_set_allocated:grpc.reflection.v1alpha.ServiceResponse.name)
 }
 
+inline const ServiceResponse* ServiceResponse::internal_default_instance() {
+  return &ServiceResponse_default_instance_.get();
+}
 #endif  // PROTOBUF_INLINE_NOT_IN_HEADERS
 
 // ===================================================================
@@ -3613,28 +3703,26 @@
 
 ErrorResponse::ErrorResponse()
   : ::google::protobuf::Message(), _internal_metadata_(NULL) {
+  if (this != internal_default_instance()) protobuf_InitDefaults_reflection_2eproto();
   SharedCtor();
   // @@protoc_insertion_point(constructor:grpc.reflection.v1alpha.ErrorResponse)
 }
 
 void ErrorResponse::InitAsDefaultInstance() {
-  _is_default_instance_ = true;
 }
 
 ErrorResponse::ErrorResponse(const ErrorResponse& from)
   : ::google::protobuf::Message(),
     _internal_metadata_(NULL) {
   SharedCtor();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
   // @@protoc_insertion_point(copy_constructor:grpc.reflection.v1alpha.ErrorResponse)
 }
 
 void ErrorResponse::SharedCtor() {
-    _is_default_instance_ = false;
-  ::google::protobuf::internal::GetEmptyString();
-  _cached_size_ = 0;
-  error_code_ = 0;
   error_message_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+  error_code_ = 0;
+  _cached_size_ = 0;
 }
 
 ErrorResponse::~ErrorResponse() {
@@ -3644,8 +3732,6 @@
 
 void ErrorResponse::SharedDtor() {
   error_message_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
-  if (this != default_instance_) {
-  }
 }
 
 void ErrorResponse::SetCachedSize(int size) const {
@@ -3659,11 +3745,11 @@
 }
 
 const ErrorResponse& ErrorResponse::default_instance() {
-  if (default_instance_ == NULL) protobuf_AddDesc_reflection_2eproto();
-  return *default_instance_;
+  protobuf_InitDefaults_reflection_2eproto();
+  return *internal_default_instance();
 }
 
-ErrorResponse* ErrorResponse::default_instance_ = NULL;
+::google::protobuf::internal::ExplicitlyConstructed<ErrorResponse> ErrorResponse_default_instance_;
 
 ErrorResponse* ErrorResponse::New(::google::protobuf::Arena* arena) const {
   ErrorResponse* n = new ErrorResponse;
@@ -3692,10 +3778,10 @@
       // optional int32 error_code = 1;
       case 1: {
         if (tag == 8) {
+
           DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
                    ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
                  input, &error_code_)));
-
         } else {
           goto handle_unusual;
         }
@@ -3762,8 +3848,9 @@
   // @@protoc_insertion_point(serialize_end:grpc.reflection.v1alpha.ErrorResponse)
 }
 
-::google::protobuf::uint8* ErrorResponse::SerializeWithCachedSizesToArray(
-    ::google::protobuf::uint8* target) const {
+::google::protobuf::uint8* ErrorResponse::InternalSerializeWithCachedSizesToArray(
+    bool deterministic, ::google::protobuf::uint8* target) const {
+  (void)deterministic; // Unused
   // @@protoc_insertion_point(serialize_to_array_start:grpc.reflection.v1alpha.ErrorResponse)
   // optional int32 error_code = 1;
   if (this->error_code() != 0) {
@@ -3785,9 +3872,9 @@
   return target;
 }
 
-int ErrorResponse::ByteSize() const {
+size_t ErrorResponse::ByteSizeLong() const {
 // @@protoc_insertion_point(message_byte_size_start:grpc.reflection.v1alpha.ErrorResponse)
-  int total_size = 0;
+  size_t total_size = 0;
 
   // optional int32 error_code = 1;
   if (this->error_code() != 0) {
@@ -3803,8 +3890,9 @@
         this->error_message());
   }
 
+  int cached_size = ::google::protobuf::internal::ToCachedSize(total_size);
   GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
-  _cached_size_ = total_size;
+  _cached_size_ = cached_size;
   GOOGLE_SAFE_CONCURRENT_WRITES_END();
   return total_size;
 }
@@ -3812,7 +3900,7 @@
 void ErrorResponse::MergeFrom(const ::google::protobuf::Message& from) {
 // @@protoc_insertion_point(generalized_merge_from_start:grpc.reflection.v1alpha.ErrorResponse)
   if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
-  const ErrorResponse* source = 
+  const ErrorResponse* source =
       ::google::protobuf::internal::DynamicCastToGenerated<const ErrorResponse>(
           &from);
   if (source == NULL) {
@@ -3820,13 +3908,21 @@
     ::google::protobuf::internal::ReflectionOps::Merge(from, this);
   } else {
   // @@protoc_insertion_point(generalized_merge_from_cast_success:grpc.reflection.v1alpha.ErrorResponse)
-    MergeFrom(*source);
+    UnsafeMergeFrom(*source);
   }
 }
 
 void ErrorResponse::MergeFrom(const ErrorResponse& from) {
 // @@protoc_insertion_point(class_specific_merge_from_start:grpc.reflection.v1alpha.ErrorResponse)
-  if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+  if (GOOGLE_PREDICT_TRUE(&from != this)) {
+    UnsafeMergeFrom(from);
+  } else {
+    MergeFromFail(__LINE__);
+  }
+}
+
+void ErrorResponse::UnsafeMergeFrom(const ErrorResponse& from) {
+  GOOGLE_DCHECK(&from != this);
   if (from.error_code() != 0) {
     set_error_code(from.error_code());
   }
@@ -3847,7 +3943,7 @@
 // @@protoc_insertion_point(class_specific_copy_from_start:grpc.reflection.v1alpha.ErrorResponse)
   if (&from == this) return;
   Clear();
-  MergeFrom(from);
+  UnsafeMergeFrom(from);
 }
 
 bool ErrorResponse::IsInitialized() const {
@@ -3881,11 +3977,11 @@
 void ErrorResponse::clear_error_code() {
   error_code_ = 0;
 }
- ::google::protobuf::int32 ErrorResponse::error_code() const {
+::google::protobuf::int32 ErrorResponse::error_code() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ErrorResponse.error_code)
   return error_code_;
 }
- void ErrorResponse::set_error_code(::google::protobuf::int32 value) {
+void ErrorResponse::set_error_code(::google::protobuf::int32 value) {
   
   error_code_ = value;
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ErrorResponse.error_code)
@@ -3895,37 +3991,37 @@
 void ErrorResponse::clear_error_message() {
   error_message_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- const ::std::string& ErrorResponse::error_message() const {
+const ::std::string& ErrorResponse::error_message() const {
   // @@protoc_insertion_point(field_get:grpc.reflection.v1alpha.ErrorResponse.error_message)
   return error_message_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- void ErrorResponse::set_error_message(const ::std::string& value) {
+void ErrorResponse::set_error_message(const ::std::string& value) {
   
   error_message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
   // @@protoc_insertion_point(field_set:grpc.reflection.v1alpha.ErrorResponse.error_message)
 }
- void ErrorResponse::set_error_message(const char* value) {
+void ErrorResponse::set_error_message(const char* value) {
   
   error_message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
   // @@protoc_insertion_point(field_set_char:grpc.reflection.v1alpha.ErrorResponse.error_message)
 }
- void ErrorResponse::set_error_message(const char* value, size_t size) {
+void ErrorResponse::set_error_message(const char* value, size_t size) {
   
   error_message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
       ::std::string(reinterpret_cast<const char*>(value), size));
   // @@protoc_insertion_point(field_set_pointer:grpc.reflection.v1alpha.ErrorResponse.error_message)
 }
- ::std::string* ErrorResponse::mutable_error_message() {
+::std::string* ErrorResponse::mutable_error_message() {
   
   // @@protoc_insertion_point(field_mutable:grpc.reflection.v1alpha.ErrorResponse.error_message)
   return error_message_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- ::std::string* ErrorResponse::release_error_message() {
+::std::string* ErrorResponse::release_error_message() {
   // @@protoc_insertion_point(field_release:grpc.reflection.v1alpha.ErrorResponse.error_message)
   
   return error_message_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
- void ErrorResponse::set_allocated_error_message(::std::string* error_message) {
+void ErrorResponse::set_allocated_error_message(::std::string* error_message) {
   if (error_message != NULL) {
     
   } else {
@@ -3935,6 +4031,9 @@
   // @@protoc_insertion_point(field_set_allocated:grpc.reflection.v1alpha.ErrorResponse.error_message)
 }
 
+inline const ErrorResponse* ErrorResponse::internal_default_instance() {
+  return &ErrorResponse_default_instance_.get();
+}
 #endif  // PROTOBUF_INLINE_NOT_IN_HEADERS
 
 // @@protoc_insertion_point(namespace_scope)
diff --git a/src/cpp/server/server.cc b/src/cpp/server/server.cc
deleted file mode 100644
index af04fd4..0000000
--- a/src/cpp/server/server.cc
+++ /dev/null
@@ -1,633 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <grpc++/server.h>
-
-#include <sstream>
-#include <utility>
-
-#include <grpc++/completion_queue.h>
-#include <grpc++/generic/async_generic_service.h>
-#include <grpc++/impl/codegen/completion_queue_tag.h>
-#include <grpc++/impl/grpc_library.h>
-#include <grpc++/impl/method_handler_impl.h>
-#include <grpc++/impl/rpc_service_method.h>
-#include <grpc++/impl/server_initializer.h>
-#include <grpc++/impl/service_type.h>
-#include <grpc++/security/server_credentials.h>
-#include <grpc++/server_context.h>
-#include <grpc++/support/time.h>
-#include <grpc/grpc.h>
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-
-#include "src/core/lib/profiling/timers.h"
-#include "src/cpp/server/thread_pool_interface.h"
-
-namespace grpc {
-
-class DefaultGlobalCallbacks GRPC_FINAL : public Server::GlobalCallbacks {
- public:
-  ~DefaultGlobalCallbacks() GRPC_OVERRIDE {}
-  void PreSynchronousRequest(ServerContext* context) GRPC_OVERRIDE {}
-  void PostSynchronousRequest(ServerContext* context) GRPC_OVERRIDE {}
-};
-
-static std::shared_ptr<Server::GlobalCallbacks> g_callbacks = nullptr;
-static gpr_once g_once_init_callbacks = GPR_ONCE_INIT;
-
-static void InitGlobalCallbacks() {
-  if (!g_callbacks) {
-    g_callbacks.reset(new DefaultGlobalCallbacks());
-  }
-}
-
-class Server::UnimplementedAsyncRequestContext {
- protected:
-  UnimplementedAsyncRequestContext() : generic_stream_(&server_context_) {}
-
-  GenericServerContext server_context_;
-  GenericServerAsyncReaderWriter generic_stream_;
-};
-
-class Server::UnimplementedAsyncRequest GRPC_FINAL
-    : public UnimplementedAsyncRequestContext,
-      public GenericAsyncRequest {
- public:
-  UnimplementedAsyncRequest(Server* server, ServerCompletionQueue* cq)
-      : GenericAsyncRequest(server, &server_context_, &generic_stream_, cq, cq,
-                            NULL, false),
-        server_(server),
-        cq_(cq) {}
-
-  bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE;
-
-  ServerContext* context() { return &server_context_; }
-  GenericServerAsyncReaderWriter* stream() { return &generic_stream_; }
-
- private:
-  Server* const server_;
-  ServerCompletionQueue* const cq_;
-};
-
-typedef SneakyCallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus>
-    UnimplementedAsyncResponseOp;
-class Server::UnimplementedAsyncResponse GRPC_FINAL
-    : public UnimplementedAsyncResponseOp {
- public:
-  UnimplementedAsyncResponse(UnimplementedAsyncRequest* request);
-  ~UnimplementedAsyncResponse() { delete request_; }
-
-  bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE {
-    bool r = UnimplementedAsyncResponseOp::FinalizeResult(tag, status);
-    delete this;
-    return r;
-  }
-
- private:
-  UnimplementedAsyncRequest* const request_;
-};
-
-class Server::ShutdownRequest GRPC_FINAL : public CompletionQueueTag {
- public:
-  bool FinalizeResult(void** tag, bool* status) {
-    delete this;
-    return false;
-  }
-};
-
-class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
- public:
-  SyncRequest(RpcServiceMethod* method, void* tag)
-      : method_(method),
-        tag_(tag),
-        in_flight_(false),
-        has_request_payload_(method->method_type() == RpcMethod::NORMAL_RPC ||
-                             method->method_type() ==
-                                 RpcMethod::SERVER_STREAMING),
-        call_details_(nullptr),
-        cq_(nullptr) {
-    grpc_metadata_array_init(&request_metadata_);
-  }
-
-  ~SyncRequest() {
-    if (call_details_) {
-      delete call_details_;
-    }
-    grpc_metadata_array_destroy(&request_metadata_);
-  }
-
-  static SyncRequest* Wait(CompletionQueue* cq, bool* ok) {
-    void* tag = nullptr;
-    *ok = false;
-    if (!cq->Next(&tag, ok)) {
-      return nullptr;
-    }
-    auto* mrd = static_cast<SyncRequest*>(tag);
-    GPR_ASSERT(mrd->in_flight_);
-    return mrd;
-  }
-
-  static bool AsyncWait(CompletionQueue* cq, SyncRequest** req, bool* ok,
-                        gpr_timespec deadline) {
-    void* tag = nullptr;
-    *ok = false;
-    switch (cq->AsyncNext(&tag, ok, deadline)) {
-      case CompletionQueue::TIMEOUT:
-        *req = nullptr;
-        return true;
-      case CompletionQueue::SHUTDOWN:
-        *req = nullptr;
-        return false;
-      case CompletionQueue::GOT_EVENT:
-        *req = static_cast<SyncRequest*>(tag);
-        GPR_ASSERT((*req)->in_flight_);
-        return true;
-    }
-    GPR_UNREACHABLE_CODE(return false);
-  }
-
-  void SetupRequest() { cq_ = grpc_completion_queue_create(nullptr); }
-
-  void TeardownRequest() {
-    grpc_completion_queue_destroy(cq_);
-    cq_ = nullptr;
-  }
-
-  void Request(grpc_server* server, grpc_completion_queue* notify_cq) {
-    GPR_ASSERT(cq_ && !in_flight_);
-    in_flight_ = true;
-    if (tag_) {
-      GPR_ASSERT(GRPC_CALL_OK ==
-                 grpc_server_request_registered_call(
-                     server, tag_, &call_, &deadline_, &request_metadata_,
-                     has_request_payload_ ? &request_payload_ : nullptr, cq_,
-                     notify_cq, this));
-    } else {
-      if (!call_details_) {
-        call_details_ = new grpc_call_details;
-        grpc_call_details_init(call_details_);
-      }
-      GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(
-                                     server, &call_, call_details_,
-                                     &request_metadata_, cq_, notify_cq, this));
-    }
-  }
-
-  bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE {
-    if (!*status) {
-      grpc_completion_queue_destroy(cq_);
-    }
-    if (call_details_) {
-      deadline_ = call_details_->deadline;
-      grpc_call_details_destroy(call_details_);
-      grpc_call_details_init(call_details_);
-    }
-    return true;
-  }
-
-  class CallData GRPC_FINAL {
-   public:
-    explicit CallData(Server* server, SyncRequest* mrd)
-        : cq_(mrd->cq_),
-          call_(mrd->call_, server, &cq_, server->max_message_size_),
-          ctx_(mrd->deadline_, mrd->request_metadata_.metadata,
-               mrd->request_metadata_.count),
-          has_request_payload_(mrd->has_request_payload_),
-          request_payload_(mrd->request_payload_),
-          method_(mrd->method_) {
-      ctx_.set_call(mrd->call_);
-      ctx_.cq_ = &cq_;
-      GPR_ASSERT(mrd->in_flight_);
-      mrd->in_flight_ = false;
-      mrd->request_metadata_.count = 0;
-    }
-
-    ~CallData() {
-      if (has_request_payload_ && request_payload_) {
-        grpc_byte_buffer_destroy(request_payload_);
-      }
-    }
-
-    void Run(std::shared_ptr<GlobalCallbacks> global_callbacks) {
-      ctx_.BeginCompletionOp(&call_);
-      global_callbacks->PreSynchronousRequest(&ctx_);
-      method_->handler()->RunHandler(MethodHandler::HandlerParameter(
-          &call_, &ctx_, request_payload_, call_.max_message_size()));
-      global_callbacks->PostSynchronousRequest(&ctx_);
-      request_payload_ = nullptr;
-      void* ignored_tag;
-      bool ignored_ok;
-      cq_.Shutdown();
-      GPR_ASSERT(cq_.Next(&ignored_tag, &ignored_ok) == false);
-    }
-
-   private:
-    CompletionQueue cq_;
-    Call call_;
-    ServerContext ctx_;
-    const bool has_request_payload_;
-    grpc_byte_buffer* request_payload_;
-    RpcServiceMethod* const method_;
-  };
-
- private:
-  RpcServiceMethod* const method_;
-  void* const tag_;
-  bool in_flight_;
-  const bool has_request_payload_;
-  uint32_t incoming_flags_;
-  grpc_call* call_;
-  grpc_call_details* call_details_;
-  gpr_timespec deadline_;
-  grpc_metadata_array request_metadata_;
-  grpc_byte_buffer* request_payload_;
-  grpc_completion_queue* cq_;
-};
-
-static internal::GrpcLibraryInitializer g_gli_initializer;
-Server::Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned,
-               int max_message_size, ChannelArguments* args)
-    : max_message_size_(max_message_size),
-      started_(false),
-      shutdown_(false),
-      shutdown_notified_(false),
-      num_running_cb_(0),
-      sync_methods_(new std::list<SyncRequest>),
-      has_generic_service_(false),
-      server_(nullptr),
-      thread_pool_(thread_pool),
-      thread_pool_owned_(thread_pool_owned),
-      server_initializer_(new ServerInitializer(this)) {
-  g_gli_initializer.summon();
-  gpr_once_init(&g_once_init_callbacks, InitGlobalCallbacks);
-  global_callbacks_ = g_callbacks;
-  global_callbacks_->UpdateArguments(args);
-  grpc_channel_args channel_args;
-  args->SetChannelArgs(&channel_args);
-  server_ = grpc_server_create(&channel_args, nullptr);
-  if (thread_pool_ == nullptr) {
-    grpc_server_register_non_listening_completion_queue(server_, cq_.cq(),
-                                                        nullptr);
-  } else {
-    grpc_server_register_completion_queue(server_, cq_.cq(), nullptr);
-  }
-}
-
-Server::~Server() {
-  {
-    grpc::unique_lock<grpc::mutex> lock(mu_);
-    if (started_ && !shutdown_) {
-      lock.unlock();
-      Shutdown();
-    } else if (!started_) {
-      cq_.Shutdown();
-    }
-  }
-  void* got_tag;
-  bool ok;
-  GPR_ASSERT(!cq_.Next(&got_tag, &ok));
-  grpc_server_destroy(server_);
-  if (thread_pool_owned_) {
-    delete thread_pool_;
-  }
-  delete sync_methods_;
-}
-
-void Server::SetGlobalCallbacks(GlobalCallbacks* callbacks) {
-  GPR_ASSERT(!g_callbacks);
-  GPR_ASSERT(callbacks);
-  g_callbacks.reset(callbacks);
-}
-
-grpc_server* Server::c_server() { return server_; }
-
-CompletionQueue* Server::completion_queue() { return &cq_; }
-
-static grpc_server_register_method_payload_handling PayloadHandlingForMethod(
-    RpcServiceMethod* method) {
-  switch (method->method_type()) {
-    case RpcMethod::NORMAL_RPC:
-    case RpcMethod::SERVER_STREAMING:
-      return GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER;
-    case RpcMethod::CLIENT_STREAMING:
-    case RpcMethod::BIDI_STREAMING:
-      return GRPC_SRM_PAYLOAD_NONE;
-  }
-  GPR_UNREACHABLE_CODE(return GRPC_SRM_PAYLOAD_NONE;);
-}
-
-bool Server::RegisterService(const grpc::string* host, Service* service) {
-  bool has_async_methods = service->has_async_methods();
-  if (has_async_methods) {
-    GPR_ASSERT(service->server_ == nullptr &&
-               "Can only register an asynchronous service against one server.");
-    service->server_ = this;
-  }
-  const char* method_name = nullptr;
-  for (auto it = service->methods_.begin(); it != service->methods_.end();
-       ++it) {
-    if (it->get() == nullptr) {  // Handled by generic service if any.
-      continue;
-    }
-    RpcServiceMethod* method = it->get();
-    void* tag = grpc_server_register_method(
-        server_, method->name(), host ? host->c_str() : nullptr,
-        PayloadHandlingForMethod(method), 0);
-    if (tag == nullptr) {
-      gpr_log(GPR_DEBUG, "Attempt to register %s multiple times",
-              method->name());
-      return false;
-    }
-    if (method->handler() == nullptr) {
-      method->set_server_tag(tag);
-    } else {
-      sync_methods_->emplace_back(method, tag);
-    }
-    method_name = method->name();
-  }
-
-  // Parse service name.
-  if (method_name != nullptr) {
-    std::stringstream ss(method_name);
-    grpc::string service_name;
-    if (std::getline(ss, service_name, '/') &&
-        std::getline(ss, service_name, '/')) {
-      services_.push_back(service_name);
-    }
-  }
-  return true;
-}
-
-void Server::RegisterAsyncGenericService(AsyncGenericService* service) {
-  GPR_ASSERT(service->server_ == nullptr &&
-             "Can only register an async generic service against one server.");
-  service->server_ = this;
-  has_generic_service_ = true;
-}
-
-int Server::AddListeningPort(const grpc::string& addr,
-                             ServerCredentials* creds) {
-  GPR_ASSERT(!started_);
-  return creds->AddPortToServer(addr, server_);
-}
-
-bool Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) {
-  GPR_ASSERT(!started_);
-  started_ = true;
-  grpc_server_start(server_);
-
-  if (!has_generic_service_) {
-    if (!sync_methods_->empty()) {
-      unknown_method_.reset(new RpcServiceMethod(
-          "unknown", RpcMethod::BIDI_STREAMING, new UnknownMethodHandler));
-      // Use of emplace_back with just constructor arguments is not accepted
-      // here by gcc-4.4 because it can't match the anonymous nullptr with a
-      // proper constructor implicitly. Construct the object and use push_back.
-      sync_methods_->push_back(SyncRequest(unknown_method_.get(), nullptr));
-    }
-    for (size_t i = 0; i < num_cqs; i++) {
-      if (cqs[i]->IsFrequentlyPolled()) {
-        new UnimplementedAsyncRequest(this, cqs[i]);
-      }
-    }
-  }
-  // Start processing rpcs.
-  if (!sync_methods_->empty()) {
-    for (auto m = sync_methods_->begin(); m != sync_methods_->end(); m++) {
-      m->SetupRequest();
-      m->Request(server_, cq_.cq());
-    }
-
-    ScheduleCallback();
-  }
-
-  return true;
-}
-
-void Server::ShutdownInternal(gpr_timespec deadline) {
-  grpc::unique_lock<grpc::mutex> lock(mu_);
-  if (started_ && !shutdown_) {
-    shutdown_ = true;
-    grpc_server_shutdown_and_notify(server_, cq_.cq(), new ShutdownRequest());
-    cq_.Shutdown();
-    lock.unlock();
-    // Spin, eating requests until the completion queue is completely shutdown.
-    // If the deadline expires then cancel anything that's pending and keep
-    // spinning forever until the work is actually drained.
-    // Since nothing else needs to touch state guarded by mu_, holding it
-    // through this loop is fine.
-    SyncRequest* request;
-    bool ok;
-    while (SyncRequest::AsyncWait(&cq_, &request, &ok, deadline)) {
-      if (request == NULL) {  // deadline expired
-        grpc_server_cancel_all_calls(server_);
-        deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
-      } else if (ok) {
-        SyncRequest::CallData call_data(this, request);
-      }
-    }
-    lock.lock();
-
-    // Wait for running callbacks to finish.
-    while (num_running_cb_ != 0) {
-      callback_cv_.wait(lock);
-    }
-
-    shutdown_notified_ = true;
-    shutdown_cv_.notify_all();
-  }
-}
-
-void Server::Wait() {
-  grpc::unique_lock<grpc::mutex> lock(mu_);
-  while (started_ && !shutdown_notified_) {
-    shutdown_cv_.wait(lock);
-  }
-}
-
-void Server::PerformOpsOnCall(CallOpSetInterface* ops, Call* call) {
-  static const size_t MAX_OPS = 8;
-  size_t nops = 0;
-  grpc_op cops[MAX_OPS];
-  ops->FillOps(cops, &nops);
-  auto result = grpc_call_start_batch(call->call(), cops, nops, ops, nullptr);
-  GPR_ASSERT(GRPC_CALL_OK == result);
-}
-
-ServerInterface::BaseAsyncRequest::BaseAsyncRequest(
-    ServerInterface* server, ServerContext* context,
-    ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, void* tag,
-    bool delete_on_finalize)
-    : server_(server),
-      context_(context),
-      stream_(stream),
-      call_cq_(call_cq),
-      tag_(tag),
-      delete_on_finalize_(delete_on_finalize),
-      call_(nullptr) {
-  memset(&initial_metadata_array_, 0, sizeof(initial_metadata_array_));
-}
-
-bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
-                                                       bool* status) {
-  if (*status) {
-    for (size_t i = 0; i < initial_metadata_array_.count; i++) {
-      context_->client_metadata_.insert(
-          std::pair<grpc::string_ref, grpc::string_ref>(
-              initial_metadata_array_.metadata[i].key,
-              grpc::string_ref(
-                  initial_metadata_array_.metadata[i].value,
-                  initial_metadata_array_.metadata[i].value_length)));
-    }
-  }
-  grpc_metadata_array_destroy(&initial_metadata_array_);
-  context_->set_call(call_);
-  context_->cq_ = call_cq_;
-  Call call(call_, server_, call_cq_, server_->max_message_size());
-  if (*status && call_) {
-    context_->BeginCompletionOp(&call);
-  }
-  // just the pointers inside call are copied here
-  stream_->BindCall(&call);
-  *tag = tag_;
-  if (delete_on_finalize_) {
-    delete this;
-  }
-  return true;
-}
-
-ServerInterface::RegisteredAsyncRequest::RegisteredAsyncRequest(
-    ServerInterface* server, ServerContext* context,
-    ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, void* tag)
-    : BaseAsyncRequest(server, context, stream, call_cq, tag, true) {}
-
-void ServerInterface::RegisteredAsyncRequest::IssueRequest(
-    void* registered_method, grpc_byte_buffer** payload,
-    ServerCompletionQueue* notification_cq) {
-  grpc_server_request_registered_call(
-      server_->server(), registered_method, &call_, &context_->deadline_,
-      &initial_metadata_array_, payload, call_cq_->cq(), notification_cq->cq(),
-      this);
-}
-
-ServerInterface::GenericAsyncRequest::GenericAsyncRequest(
-    ServerInterface* server, GenericServerContext* context,
-    ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
-    ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize)
-    : BaseAsyncRequest(server, context, stream, call_cq, tag,
-                       delete_on_finalize) {
-  grpc_call_details_init(&call_details_);
-  GPR_ASSERT(notification_cq);
-  GPR_ASSERT(call_cq);
-  grpc_server_request_call(server->server(), &call_, &call_details_,
-                           &initial_metadata_array_, call_cq->cq(),
-                           notification_cq->cq(), this);
-}
-
-bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag,
-                                                          bool* status) {
-  // TODO(yangg) remove the copy here.
-  if (*status) {
-    static_cast<GenericServerContext*>(context_)->method_ =
-        call_details_.method;
-    static_cast<GenericServerContext*>(context_)->host_ = call_details_.host;
-  }
-  gpr_free(call_details_.method);
-  gpr_free(call_details_.host);
-  return BaseAsyncRequest::FinalizeResult(tag, status);
-}
-
-bool Server::UnimplementedAsyncRequest::FinalizeResult(void** tag,
-                                                       bool* status) {
-  if (GenericAsyncRequest::FinalizeResult(tag, status) && *status) {
-    new UnimplementedAsyncRequest(server_, cq_);
-    new UnimplementedAsyncResponse(this);
-  } else {
-    delete this;
-  }
-  return false;
-}
-
-Server::UnimplementedAsyncResponse::UnimplementedAsyncResponse(
-    UnimplementedAsyncRequest* request)
-    : request_(request) {
-  Status status(StatusCode::UNIMPLEMENTED, "");
-  UnknownMethodHandler::FillOps(request_->context(), this);
-  request_->stream()->call_.PerformOps(this);
-}
-
-void Server::ScheduleCallback() {
-  {
-    grpc::unique_lock<grpc::mutex> lock(mu_);
-    num_running_cb_++;
-  }
-  thread_pool_->Add(std::bind(&Server::RunRpc, this));
-}
-
-void Server::RunRpc() {
-  // Wait for one more incoming rpc.
-  bool ok;
-  GPR_TIMER_SCOPE("Server::RunRpc", 0);
-  auto* mrd = SyncRequest::Wait(&cq_, &ok);
-  if (mrd) {
-    ScheduleCallback();
-    if (ok) {
-      SyncRequest::CallData cd(this, mrd);
-      {
-        mrd->SetupRequest();
-        grpc::unique_lock<grpc::mutex> lock(mu_);
-        if (!shutdown_) {
-          mrd->Request(server_, cq_.cq());
-        } else {
-          // destroy the structure that was created
-          mrd->TeardownRequest();
-        }
-      }
-      GPR_TIMER_SCOPE("cd.Run()", 0);
-      cd.Run(global_callbacks_);
-    }
-  }
-
-  {
-    grpc::unique_lock<grpc::mutex> lock(mu_);
-    num_running_cb_--;
-    if (shutdown_) {
-      callback_cv_.notify_all();
-    }
-  }
-}
-
-ServerInitializer* Server::initializer() { return server_initializer_.get(); }
-
-}  // namespace grpc
diff --git a/src/cpp/server/server_builder.cc b/src/cpp/server/server_builder.cc
index 45bb858..2980b16 100644
--- a/src/cpp/server/server_builder.cc
+++ b/src/cpp/server/server_builder.cc
@@ -35,10 +35,9 @@
 
 #include <grpc++/impl/service_type.h>
 #include <grpc++/server.h>
-#include <grpc/support/cpu.h>
 #include <grpc/support/log.h>
+#include <grpc/support/useful.h>
 
-#include "include/grpc/support/useful.h"
 #include "src/cpp/server/thread_pool_interface.h"
 
 namespace grpc {
@@ -53,7 +52,9 @@
 }
 
 ServerBuilder::ServerBuilder()
-    : max_message_size_(-1), generic_service_(nullptr) {
+    : max_receive_message_size_(-1),
+      max_send_message_size_(-1),
+      generic_service_(nullptr) {
   gpr_once_init(&once_init_plugin_list, do_plugin_list_init);
   for (auto it = g_plugin_factory_list->begin();
        it != g_plugin_factory_list->end(); it++) {
@@ -154,17 +155,18 @@
     (*option)->UpdateArguments(&args);
     (*option)->UpdatePlugins(&plugins_);
   }
-  if (!thread_pool) {
-    for (auto plugin = plugins_.begin(); plugin != plugins_.end(); plugin++) {
-      if ((*plugin)->has_sync_methods()) {
-        thread_pool.reset(CreateDefaultThreadPool());
-        has_sync_methods = true;
-        break;
-      }
+  for (auto plugin = plugins_.begin(); plugin != plugins_.end(); plugin++) {
+    if (!thread_pool && (*plugin)->has_sync_methods()) {
+      thread_pool.reset(CreateDefaultThreadPool());
+      has_sync_methods = true;
     }
+    (*plugin)->UpdateChannelArguments(&args);
   }
-  if (max_message_size_ > 0) {
-    args.SetInt(GRPC_ARG_MAX_MESSAGE_LENGTH, max_message_size_);
+  if (max_receive_message_size_ >= 0) {
+    args.SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, max_receive_message_size_);
+  }
+  if (max_send_message_size_ >= 0) {
+    args.SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, max_send_message_size_);
   }
   args.SetInt(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET,
               enabled_compression_algorithms_bitset_);
@@ -176,8 +178,8 @@
     args.SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM,
                 maybe_default_compression_algorithm_.algorithm);
   }
-  std::unique_ptr<Server> server(
-      new Server(thread_pool.release(), true, max_message_size_, &args));
+  std::unique_ptr<Server> server(new Server(thread_pool.release(), true,
+                                            max_receive_message_size_, &args));
   ServerInitializer* initializer = server->initializer();
 
   // If the server has atleast one sync methods, we know that this is a Sync
diff --git a/src/cpp/server/server_cc.cc b/src/cpp/server/server_cc.cc
new file mode 100644
index 0000000..3f89275
--- /dev/null
+++ b/src/cpp/server/server_cc.cc
@@ -0,0 +1,631 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <grpc++/server.h>
+
+#include <sstream>
+#include <utility>
+
+#include <grpc++/completion_queue.h>
+#include <grpc++/generic/async_generic_service.h>
+#include <grpc++/impl/codegen/completion_queue_tag.h>
+#include <grpc++/impl/grpc_library.h>
+#include <grpc++/impl/method_handler_impl.h>
+#include <grpc++/impl/rpc_service_method.h>
+#include <grpc++/impl/server_initializer.h>
+#include <grpc++/impl/service_type.h>
+#include <grpc++/security/server_credentials.h>
+#include <grpc++/server_context.h>
+#include <grpc++/support/time.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/profiling/timers.h"
+#include "src/cpp/server/thread_pool_interface.h"
+
+namespace grpc {
+
+class DefaultGlobalCallbacks GRPC_FINAL : public Server::GlobalCallbacks {
+ public:
+  ~DefaultGlobalCallbacks() GRPC_OVERRIDE {}
+  void PreSynchronousRequest(ServerContext* context) GRPC_OVERRIDE {}
+  void PostSynchronousRequest(ServerContext* context) GRPC_OVERRIDE {}
+};
+
+static std::shared_ptr<Server::GlobalCallbacks> g_callbacks = nullptr;
+static gpr_once g_once_init_callbacks = GPR_ONCE_INIT;
+
+static void InitGlobalCallbacks() {
+  if (!g_callbacks) {
+    g_callbacks.reset(new DefaultGlobalCallbacks());
+  }
+}
+
+class Server::UnimplementedAsyncRequestContext {
+ protected:
+  UnimplementedAsyncRequestContext() : generic_stream_(&server_context_) {}
+
+  GenericServerContext server_context_;
+  GenericServerAsyncReaderWriter generic_stream_;
+};
+
+class Server::UnimplementedAsyncRequest GRPC_FINAL
+    : public UnimplementedAsyncRequestContext,
+      public GenericAsyncRequest {
+ public:
+  UnimplementedAsyncRequest(Server* server, ServerCompletionQueue* cq)
+      : GenericAsyncRequest(server, &server_context_, &generic_stream_, cq, cq,
+                            NULL, false),
+        server_(server),
+        cq_(cq) {}
+
+  bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE;
+
+  ServerContext* context() { return &server_context_; }
+  GenericServerAsyncReaderWriter* stream() { return &generic_stream_; }
+
+ private:
+  Server* const server_;
+  ServerCompletionQueue* const cq_;
+};
+
+typedef SneakyCallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus>
+    UnimplementedAsyncResponseOp;
+class Server::UnimplementedAsyncResponse GRPC_FINAL
+    : public UnimplementedAsyncResponseOp {
+ public:
+  UnimplementedAsyncResponse(UnimplementedAsyncRequest* request);
+  ~UnimplementedAsyncResponse() { delete request_; }
+
+  bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE {
+    bool r = UnimplementedAsyncResponseOp::FinalizeResult(tag, status);
+    delete this;
+    return r;
+  }
+
+ private:
+  UnimplementedAsyncRequest* const request_;
+};
+
+class Server::ShutdownRequest GRPC_FINAL : public CompletionQueueTag {
+ public:
+  bool FinalizeResult(void** tag, bool* status) {
+    delete this;
+    return false;
+  }
+};
+
+class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
+ public:
+  SyncRequest(RpcServiceMethod* method, void* tag)
+      : method_(method),
+        tag_(tag),
+        in_flight_(false),
+        has_request_payload_(method->method_type() == RpcMethod::NORMAL_RPC ||
+                             method->method_type() ==
+                                 RpcMethod::SERVER_STREAMING),
+        call_details_(nullptr),
+        cq_(nullptr) {
+    grpc_metadata_array_init(&request_metadata_);
+  }
+
+  ~SyncRequest() {
+    if (call_details_) {
+      delete call_details_;
+    }
+    grpc_metadata_array_destroy(&request_metadata_);
+  }
+
+  static SyncRequest* Wait(CompletionQueue* cq, bool* ok) {
+    void* tag = nullptr;
+    *ok = false;
+    if (!cq->Next(&tag, ok)) {
+      return nullptr;
+    }
+    auto* mrd = static_cast<SyncRequest*>(tag);
+    GPR_ASSERT(mrd->in_flight_);
+    return mrd;
+  }
+
+  static bool AsyncWait(CompletionQueue* cq, SyncRequest** req, bool* ok,
+                        gpr_timespec deadline) {
+    void* tag = nullptr;
+    *ok = false;
+    switch (cq->AsyncNext(&tag, ok, deadline)) {
+      case CompletionQueue::TIMEOUT:
+        *req = nullptr;
+        return true;
+      case CompletionQueue::SHUTDOWN:
+        *req = nullptr;
+        return false;
+      case CompletionQueue::GOT_EVENT:
+        *req = static_cast<SyncRequest*>(tag);
+        GPR_ASSERT((*req)->in_flight_);
+        return true;
+    }
+    GPR_UNREACHABLE_CODE(return false);
+  }
+
+  void SetupRequest() { cq_ = grpc_completion_queue_create(nullptr); }
+
+  void TeardownRequest() {
+    grpc_completion_queue_destroy(cq_);
+    cq_ = nullptr;
+  }
+
+  void Request(grpc_server* server, grpc_completion_queue* notify_cq) {
+    GPR_ASSERT(cq_ && !in_flight_);
+    in_flight_ = true;
+    if (tag_) {
+      GPR_ASSERT(GRPC_CALL_OK ==
+                 grpc_server_request_registered_call(
+                     server, tag_, &call_, &deadline_, &request_metadata_,
+                     has_request_payload_ ? &request_payload_ : nullptr, cq_,
+                     notify_cq, this));
+    } else {
+      if (!call_details_) {
+        call_details_ = new grpc_call_details;
+        grpc_call_details_init(call_details_);
+      }
+      GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(
+                                     server, &call_, call_details_,
+                                     &request_metadata_, cq_, notify_cq, this));
+    }
+  }
+
+  bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE {
+    if (!*status) {
+      grpc_completion_queue_destroy(cq_);
+    }
+    if (call_details_) {
+      deadline_ = call_details_->deadline;
+      grpc_call_details_destroy(call_details_);
+      grpc_call_details_init(call_details_);
+    }
+    return true;
+  }
+
+  class CallData GRPC_FINAL {
+   public:
+    explicit CallData(Server* server, SyncRequest* mrd)
+        : cq_(mrd->cq_),
+          call_(mrd->call_, server, &cq_, server->max_receive_message_size_),
+          ctx_(mrd->deadline_, mrd->request_metadata_.metadata,
+               mrd->request_metadata_.count),
+          has_request_payload_(mrd->has_request_payload_),
+          request_payload_(mrd->request_payload_),
+          method_(mrd->method_) {
+      ctx_.set_call(mrd->call_);
+      ctx_.cq_ = &cq_;
+      GPR_ASSERT(mrd->in_flight_);
+      mrd->in_flight_ = false;
+      mrd->request_metadata_.count = 0;
+    }
+
+    ~CallData() {
+      if (has_request_payload_ && request_payload_) {
+        grpc_byte_buffer_destroy(request_payload_);
+      }
+    }
+
+    void Run(std::shared_ptr<GlobalCallbacks> global_callbacks) {
+      ctx_.BeginCompletionOp(&call_);
+      global_callbacks->PreSynchronousRequest(&ctx_);
+      method_->handler()->RunHandler(MethodHandler::HandlerParameter(
+          &call_, &ctx_, request_payload_, call_.max_receive_message_size()));
+      global_callbacks->PostSynchronousRequest(&ctx_);
+      request_payload_ = nullptr;
+      void* ignored_tag;
+      bool ignored_ok;
+      cq_.Shutdown();
+      GPR_ASSERT(cq_.Next(&ignored_tag, &ignored_ok) == false);
+    }
+
+   private:
+    CompletionQueue cq_;
+    Call call_;
+    ServerContext ctx_;
+    const bool has_request_payload_;
+    grpc_byte_buffer* request_payload_;
+    RpcServiceMethod* const method_;
+  };
+
+ private:
+  RpcServiceMethod* const method_;
+  void* const tag_;
+  bool in_flight_;
+  const bool has_request_payload_;
+  uint32_t incoming_flags_;
+  grpc_call* call_;
+  grpc_call_details* call_details_;
+  gpr_timespec deadline_;
+  grpc_metadata_array request_metadata_;
+  grpc_byte_buffer* request_payload_;
+  grpc_completion_queue* cq_;
+};
+
+static internal::GrpcLibraryInitializer g_gli_initializer;
+Server::Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned,
+               int max_receive_message_size, ChannelArguments* args)
+    : max_receive_message_size_(max_receive_message_size),
+      started_(false),
+      shutdown_(false),
+      shutdown_notified_(false),
+      num_running_cb_(0),
+      sync_methods_(new std::list<SyncRequest>),
+      has_generic_service_(false),
+      server_(nullptr),
+      thread_pool_(thread_pool),
+      thread_pool_owned_(thread_pool_owned),
+      server_initializer_(new ServerInitializer(this)) {
+  g_gli_initializer.summon();
+  gpr_once_init(&g_once_init_callbacks, InitGlobalCallbacks);
+  global_callbacks_ = g_callbacks;
+  global_callbacks_->UpdateArguments(args);
+  grpc_channel_args channel_args;
+  args->SetChannelArgs(&channel_args);
+  server_ = grpc_server_create(&channel_args, nullptr);
+  if (thread_pool_ == nullptr) {
+    grpc_server_register_non_listening_completion_queue(server_, cq_.cq(),
+                                                        nullptr);
+  } else {
+    grpc_server_register_completion_queue(server_, cq_.cq(), nullptr);
+  }
+}
+
+Server::~Server() {
+  {
+    grpc::unique_lock<grpc::mutex> lock(mu_);
+    if (started_ && !shutdown_) {
+      lock.unlock();
+      Shutdown();
+    } else if (!started_) {
+      cq_.Shutdown();
+    }
+  }
+  void* got_tag;
+  bool ok;
+  GPR_ASSERT(!cq_.Next(&got_tag, &ok));
+  grpc_server_destroy(server_);
+  if (thread_pool_owned_) {
+    delete thread_pool_;
+  }
+  delete sync_methods_;
+}
+
+void Server::SetGlobalCallbacks(GlobalCallbacks* callbacks) {
+  GPR_ASSERT(!g_callbacks);
+  GPR_ASSERT(callbacks);
+  g_callbacks.reset(callbacks);
+}
+
+grpc_server* Server::c_server() { return server_; }
+
+static grpc_server_register_method_payload_handling PayloadHandlingForMethod(
+    RpcServiceMethod* method) {
+  switch (method->method_type()) {
+    case RpcMethod::NORMAL_RPC:
+    case RpcMethod::SERVER_STREAMING:
+      return GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER;
+    case RpcMethod::CLIENT_STREAMING:
+    case RpcMethod::BIDI_STREAMING:
+      return GRPC_SRM_PAYLOAD_NONE;
+  }
+  GPR_UNREACHABLE_CODE(return GRPC_SRM_PAYLOAD_NONE;);
+}
+
+bool Server::RegisterService(const grpc::string* host, Service* service) {
+  bool has_async_methods = service->has_async_methods();
+  if (has_async_methods) {
+    GPR_ASSERT(service->server_ == nullptr &&
+               "Can only register an asynchronous service against one server.");
+    service->server_ = this;
+  }
+  const char* method_name = nullptr;
+  for (auto it = service->methods_.begin(); it != service->methods_.end();
+       ++it) {
+    if (it->get() == nullptr) {  // Handled by generic service if any.
+      continue;
+    }
+    RpcServiceMethod* method = it->get();
+    void* tag = grpc_server_register_method(
+        server_, method->name(), host ? host->c_str() : nullptr,
+        PayloadHandlingForMethod(method), 0);
+    if (tag == nullptr) {
+      gpr_log(GPR_DEBUG, "Attempt to register %s multiple times",
+              method->name());
+      return false;
+    }
+    if (method->handler() == nullptr) {
+      method->set_server_tag(tag);
+    } else {
+      sync_methods_->emplace_back(method, tag);
+    }
+    method_name = method->name();
+  }
+
+  // Parse service name.
+  if (method_name != nullptr) {
+    std::stringstream ss(method_name);
+    grpc::string service_name;
+    if (std::getline(ss, service_name, '/') &&
+        std::getline(ss, service_name, '/')) {
+      services_.push_back(service_name);
+    }
+  }
+  return true;
+}
+
+void Server::RegisterAsyncGenericService(AsyncGenericService* service) {
+  GPR_ASSERT(service->server_ == nullptr &&
+             "Can only register an async generic service against one server.");
+  service->server_ = this;
+  has_generic_service_ = true;
+}
+
+int Server::AddListeningPort(const grpc::string& addr,
+                             ServerCredentials* creds) {
+  GPR_ASSERT(!started_);
+  return creds->AddPortToServer(addr, server_);
+}
+
+bool Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) {
+  GPR_ASSERT(!started_);
+  started_ = true;
+  grpc_server_start(server_);
+
+  if (!has_generic_service_) {
+    if (!sync_methods_->empty()) {
+      unknown_method_.reset(new RpcServiceMethod(
+          "unknown", RpcMethod::BIDI_STREAMING, new UnknownMethodHandler));
+      // Use of emplace_back with just constructor arguments is not accepted
+      // here by gcc-4.4 because it can't match the anonymous nullptr with a
+      // proper constructor implicitly. Construct the object and use push_back.
+      sync_methods_->push_back(SyncRequest(unknown_method_.get(), nullptr));
+    }
+    for (size_t i = 0; i < num_cqs; i++) {
+      if (cqs[i]->IsFrequentlyPolled()) {
+        new UnimplementedAsyncRequest(this, cqs[i]);
+      }
+    }
+  }
+  // Start processing rpcs.
+  if (!sync_methods_->empty()) {
+    for (auto m = sync_methods_->begin(); m != sync_methods_->end(); m++) {
+      m->SetupRequest();
+      m->Request(server_, cq_.cq());
+    }
+
+    ScheduleCallback();
+  }
+
+  return true;
+}
+
+void Server::ShutdownInternal(gpr_timespec deadline) {
+  grpc::unique_lock<grpc::mutex> lock(mu_);
+  if (started_ && !shutdown_) {
+    shutdown_ = true;
+    grpc_server_shutdown_and_notify(server_, cq_.cq(), new ShutdownRequest());
+    cq_.Shutdown();
+    lock.unlock();
+    // Spin, eating requests until the completion queue is completely shutdown.
+    // If the deadline expires then cancel anything that's pending and keep
+    // spinning forever until the work is actually drained.
+    // Since nothing else needs to touch state guarded by mu_, holding it
+    // through this loop is fine.
+    SyncRequest* request;
+    bool ok;
+    while (SyncRequest::AsyncWait(&cq_, &request, &ok, deadline)) {
+      if (request == NULL) {  // deadline expired
+        grpc_server_cancel_all_calls(server_);
+        deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+      } else if (ok) {
+        SyncRequest::CallData call_data(this, request);
+      }
+    }
+    lock.lock();
+
+    // Wait for running callbacks to finish.
+    while (num_running_cb_ != 0) {
+      callback_cv_.wait(lock);
+    }
+
+    shutdown_notified_ = true;
+    shutdown_cv_.notify_all();
+  }
+}
+
+void Server::Wait() {
+  grpc::unique_lock<grpc::mutex> lock(mu_);
+  while (started_ && !shutdown_notified_) {
+    shutdown_cv_.wait(lock);
+  }
+}
+
+void Server::PerformOpsOnCall(CallOpSetInterface* ops, Call* call) {
+  static const size_t MAX_OPS = 8;
+  size_t nops = 0;
+  grpc_op cops[MAX_OPS];
+  ops->FillOps(cops, &nops);
+  auto result = grpc_call_start_batch(call->call(), cops, nops, ops, nullptr);
+  GPR_ASSERT(GRPC_CALL_OK == result);
+}
+
+ServerInterface::BaseAsyncRequest::BaseAsyncRequest(
+    ServerInterface* server, ServerContext* context,
+    ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, void* tag,
+    bool delete_on_finalize)
+    : server_(server),
+      context_(context),
+      stream_(stream),
+      call_cq_(call_cq),
+      tag_(tag),
+      delete_on_finalize_(delete_on_finalize),
+      call_(nullptr) {
+  memset(&initial_metadata_array_, 0, sizeof(initial_metadata_array_));
+}
+
+bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
+                                                       bool* status) {
+  if (*status) {
+    for (size_t i = 0; i < initial_metadata_array_.count; i++) {
+      context_->client_metadata_.insert(
+          std::pair<grpc::string_ref, grpc::string_ref>(
+              initial_metadata_array_.metadata[i].key,
+              grpc::string_ref(
+                  initial_metadata_array_.metadata[i].value,
+                  initial_metadata_array_.metadata[i].value_length)));
+    }
+  }
+  grpc_metadata_array_destroy(&initial_metadata_array_);
+  context_->set_call(call_);
+  context_->cq_ = call_cq_;
+  Call call(call_, server_, call_cq_, server_->max_receive_message_size());
+  if (*status && call_) {
+    context_->BeginCompletionOp(&call);
+  }
+  // just the pointers inside call are copied here
+  stream_->BindCall(&call);
+  *tag = tag_;
+  if (delete_on_finalize_) {
+    delete this;
+  }
+  return true;
+}
+
+ServerInterface::RegisteredAsyncRequest::RegisteredAsyncRequest(
+    ServerInterface* server, ServerContext* context,
+    ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, void* tag)
+    : BaseAsyncRequest(server, context, stream, call_cq, tag, true) {}
+
+void ServerInterface::RegisteredAsyncRequest::IssueRequest(
+    void* registered_method, grpc_byte_buffer** payload,
+    ServerCompletionQueue* notification_cq) {
+  grpc_server_request_registered_call(
+      server_->server(), registered_method, &call_, &context_->deadline_,
+      &initial_metadata_array_, payload, call_cq_->cq(), notification_cq->cq(),
+      this);
+}
+
+ServerInterface::GenericAsyncRequest::GenericAsyncRequest(
+    ServerInterface* server, GenericServerContext* context,
+    ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+    ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize)
+    : BaseAsyncRequest(server, context, stream, call_cq, tag,
+                       delete_on_finalize) {
+  grpc_call_details_init(&call_details_);
+  GPR_ASSERT(notification_cq);
+  GPR_ASSERT(call_cq);
+  grpc_server_request_call(server->server(), &call_, &call_details_,
+                           &initial_metadata_array_, call_cq->cq(),
+                           notification_cq->cq(), this);
+}
+
+bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag,
+                                                          bool* status) {
+  // TODO(yangg) remove the copy here.
+  if (*status) {
+    static_cast<GenericServerContext*>(context_)->method_ =
+        call_details_.method;
+    static_cast<GenericServerContext*>(context_)->host_ = call_details_.host;
+  }
+  gpr_free(call_details_.method);
+  gpr_free(call_details_.host);
+  return BaseAsyncRequest::FinalizeResult(tag, status);
+}
+
+bool Server::UnimplementedAsyncRequest::FinalizeResult(void** tag,
+                                                       bool* status) {
+  if (GenericAsyncRequest::FinalizeResult(tag, status) && *status) {
+    new UnimplementedAsyncRequest(server_, cq_);
+    new UnimplementedAsyncResponse(this);
+  } else {
+    delete this;
+  }
+  return false;
+}
+
+Server::UnimplementedAsyncResponse::UnimplementedAsyncResponse(
+    UnimplementedAsyncRequest* request)
+    : request_(request) {
+  Status status(StatusCode::UNIMPLEMENTED, "");
+  UnknownMethodHandler::FillOps(request_->context(), this);
+  request_->stream()->call_.PerformOps(this);
+}
+
+void Server::ScheduleCallback() {
+  {
+    grpc::unique_lock<grpc::mutex> lock(mu_);
+    num_running_cb_++;
+  }
+  thread_pool_->Add(std::bind(&Server::RunRpc, this));
+}
+
+void Server::RunRpc() {
+  // Wait for one more incoming rpc.
+  bool ok;
+  GPR_TIMER_SCOPE("Server::RunRpc", 0);
+  auto* mrd = SyncRequest::Wait(&cq_, &ok);
+  if (mrd) {
+    ScheduleCallback();
+    if (ok) {
+      SyncRequest::CallData cd(this, mrd);
+      {
+        mrd->SetupRequest();
+        grpc::unique_lock<grpc::mutex> lock(mu_);
+        if (!shutdown_) {
+          mrd->Request(server_, cq_.cq());
+        } else {
+          // destroy the structure that was created
+          mrd->TeardownRequest();
+        }
+      }
+      GPR_TIMER_SCOPE("cd.Run()", 0);
+      cd.Run(global_callbacks_);
+    }
+  }
+
+  {
+    grpc::unique_lock<grpc::mutex> lock(mu_);
+    num_running_cb_--;
+    if (shutdown_) {
+      callback_cv_.notify_all();
+    }
+  }
+}
+
+ServerInitializer* Server::initializer() { return server_initializer_.get(); }
+
+}  // namespace grpc
diff --git a/src/cpp/server/server_posix.cc b/src/cpp/server/server_posix.cc
index c3aa2ad..33d42a8 100644
--- a/src/cpp/server/server_posix.cc
+++ b/src/cpp/server/server_posix.cc
@@ -40,8 +40,7 @@
 #ifdef GPR_SUPPORT_CHANNELS_FROM_FD
 
 void AddInsecureChannelFromFd(Server* server, int fd) {
-  grpc_server_add_insecure_channel_from_fd(
-      server->c_server(), server->completion_queue()->cq(), fd);
+  grpc_server_add_insecure_channel_from_fd(server->c_server(), NULL, fd);
 }
 
 #endif  // GPR_SUPPORT_CHANNELS_FROM_FD
diff --git a/src/cpp/util/byte_buffer.cc b/src/cpp/util/byte_buffer.cc
deleted file mode 100644
index c2cd20e..0000000
--- a/src/cpp/util/byte_buffer.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <grpc++/support/byte_buffer.h>
-#include <grpc/byte_buffer_reader.h>
-
-namespace grpc {
-
-ByteBuffer::ByteBuffer(const Slice* slices, size_t nslices) {
-  // TODO(yangg) maybe expose some core API to simplify this
-  std::vector<gpr_slice> c_slices(nslices);
-  for (size_t i = 0; i < nslices; i++) {
-    c_slices[i] = slices[i].slice_;
-  }
-  buffer_ = grpc_raw_byte_buffer_create(c_slices.data(), nslices);
-}
-
-ByteBuffer::~ByteBuffer() {
-  if (buffer_) {
-    grpc_byte_buffer_destroy(buffer_);
-  }
-}
-
-void ByteBuffer::Clear() {
-  if (buffer_) {
-    grpc_byte_buffer_destroy(buffer_);
-    buffer_ = nullptr;
-  }
-}
-
-Status ByteBuffer::Dump(std::vector<Slice>* slices) const {
-  slices->clear();
-  if (!buffer_) {
-    return Status(StatusCode::FAILED_PRECONDITION, "Buffer not initialized");
-  }
-  grpc_byte_buffer_reader reader;
-  if (!grpc_byte_buffer_reader_init(&reader, buffer_)) {
-    return Status(StatusCode::INTERNAL,
-                  "Couldn't initialize byte buffer reader");
-  }
-  gpr_slice s;
-  while (grpc_byte_buffer_reader_next(&reader, &s)) {
-    slices->push_back(Slice(s, Slice::STEAL_REF));
-  }
-  grpc_byte_buffer_reader_destroy(&reader);
-  return Status::OK;
-}
-
-size_t ByteBuffer::Length() const {
-  if (buffer_) {
-    return grpc_byte_buffer_length(buffer_);
-  } else {
-    return 0;
-  }
-}
-
-ByteBuffer::ByteBuffer(const ByteBuffer& buf)
-    : buffer_(grpc_byte_buffer_copy(buf.buffer_)) {}
-
-ByteBuffer& ByteBuffer::operator=(const ByteBuffer& buf) {
-  Clear();  // first remove existing data
-  if (buf.buffer_) {
-    buffer_ = grpc_byte_buffer_copy(buf.buffer_);  // then copy
-  }
-  return *this;
-}
-
-}  // namespace grpc
diff --git a/src/cpp/util/byte_buffer_cc.cc b/src/cpp/util/byte_buffer_cc.cc
new file mode 100644
index 0000000..91ed66b
--- /dev/null
+++ b/src/cpp/util/byte_buffer_cc.cc
@@ -0,0 +1,111 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <grpc++/support/byte_buffer.h>
+#include <grpc/byte_buffer_reader.h>
+
+namespace grpc {
+
+ByteBuffer::ByteBuffer(const Slice* slices, size_t nslices) {
+  // The following assertions check that the representation of a grpc::Slice is
+  // identical to that of a gpr_slice:  it has a gpr_slice field, and nothing
+  // else.
+  static_assert(std::is_same<decltype(slices[0].slice_), gpr_slice>::value,
+                "Slice must have same representation as gpr_slice");
+  static_assert(sizeof(Slice) == sizeof(gpr_slice),
+                "Slice must have same representation as gpr_slice");
+  // The const_cast is legal if grpc_raw_byte_buffer_create() does no more
+  // than its advertised side effect of increasing the reference count of the
+  // slices it processes, and such an increase does not affect the semantics
+  // seen by the caller of this constructor.
+  buffer_ = grpc_raw_byte_buffer_create(
+      reinterpret_cast<gpr_slice*>(const_cast<Slice*>(slices)), nslices);
+}
+
+ByteBuffer::~ByteBuffer() {
+  if (buffer_) {
+    grpc_byte_buffer_destroy(buffer_);
+  }
+}
+
+void ByteBuffer::Clear() {
+  if (buffer_) {
+    grpc_byte_buffer_destroy(buffer_);
+    buffer_ = nullptr;
+  }
+}
+
+Status ByteBuffer::Dump(std::vector<Slice>* slices) const {
+  slices->clear();
+  if (!buffer_) {
+    return Status(StatusCode::FAILED_PRECONDITION, "Buffer not initialized");
+  }
+  grpc_byte_buffer_reader reader;
+  if (!grpc_byte_buffer_reader_init(&reader, buffer_)) {
+    return Status(StatusCode::INTERNAL,
+                  "Couldn't initialize byte buffer reader");
+  }
+  gpr_slice s;
+  while (grpc_byte_buffer_reader_next(&reader, &s)) {
+    slices->push_back(Slice(s, Slice::STEAL_REF));
+  }
+  grpc_byte_buffer_reader_destroy(&reader);
+  return Status::OK;
+}
+
+size_t ByteBuffer::Length() const {
+  if (buffer_) {
+    return grpc_byte_buffer_length(buffer_);
+  } else {
+    return 0;
+  }
+}
+
+ByteBuffer::ByteBuffer(const ByteBuffer& buf)
+    : buffer_(grpc_byte_buffer_copy(buf.buffer_)) {}
+
+ByteBuffer& ByteBuffer::operator=(const ByteBuffer& buf) {
+  Clear();  // first remove existing data
+  if (buf.buffer_) {
+    buffer_ = grpc_byte_buffer_copy(buf.buffer_);  // then copy
+  }
+  return *this;
+}
+
+void ByteBuffer::Swap(ByteBuffer* other) {
+  grpc_byte_buffer* tmp = other->buffer_;
+  other->buffer_ = buffer_;
+  buffer_ = tmp;
+}
+
+}  // namespace grpc
diff --git a/src/cpp/util/slice.cc b/src/cpp/util/slice_cc.cc
similarity index 100%
rename from src/cpp/util/slice.cc
rename to src/cpp/util/slice_cc.cc
diff --git a/src/cpp/util/time.cc b/src/cpp/util/time_cc.cc
similarity index 100%
rename from src/cpp/util/time.cc
rename to src/cpp/util/time_cc.cc
diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.csproj b/src/csharp/Grpc.Auth/Grpc.Auth.csproj
index 1fa14fc..a44aaf1 100644
--- a/src/csharp/Grpc.Auth/Grpc.Auth.csproj
+++ b/src/csharp/Grpc.Auth/Grpc.Auth.csproj
@@ -39,30 +39,37 @@
     <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Auth, Version=1.11.1.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Apis.Auth.1.11.1\lib\net45\Google.Apis.Auth.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.11.1.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Apis.Auth.1.11.1\lib\net45\Google.Apis.Auth.PlatformServices.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Core, Version=1.11.1.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Apis.Core.1.11.1\lib\net45\Google.Apis.Core.dll</HintPath>
-    </Reference>
-    <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Net" />
     <Reference Include="System.Net.Http" />
     <Reference Include="System.Net.Http.WebRequest" />
+    <Reference Include="BouncyCastle.Crypto">
+      <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json">
+      <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="log4net">
+      <HintPath>..\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Core">
+      <HintPath>..\packages\Google.Apis.Core.1.16.0\lib\net45\Google.Apis.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="Zlib.Portable">
+      <HintPath>..\packages\Zlib.Portable.Signed.1.11.0\lib\portable-net4+sl5+wp8+win8+wpa81+MonoTouch+MonoAndroid\Zlib.Portable.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis">
+      <HintPath>..\packages\Google.Apis.1.16.0\lib\net45\Google.Apis.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.PlatformServices">
+      <HintPath>..\packages\Google.Apis.1.16.0\lib\net45\Google.Apis.PlatformServices.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth">
+      <HintPath>..\packages\Google.Apis.Auth.1.16.0\lib\net45\Google.Apis.Auth.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth.PlatformServices">
+      <HintPath>..\packages\Google.Apis.Auth.1.16.0\lib\net45\Google.Apis.Auth.PlatformServices.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\Grpc.Core\Version.cs">
diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.nuspec b/src/csharp/Grpc.Auth/Grpc.Auth.nuspec
index 4baed37..a1f5668 100644
--- a/src/csharp/Grpc.Auth/Grpc.Auth.nuspec
+++ b/src/csharp/Grpc.Auth/Grpc.Auth.nuspec
@@ -15,7 +15,7 @@
     <copyright>Copyright 2015, Google Inc.</copyright>
     <tags>gRPC RPC Protocol HTTP/2 Auth OAuth2</tags>
 	<dependencies>
-	  <dependency id="Google.Apis.Auth" version="1.11.1" />
+	  <dependency id="Google.Apis.Auth" version="1.15.0" />
 	  <dependency id="Grpc.Core" version="$version$" />
     </dependencies>
   </metadata>
diff --git a/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs
index 83396c5..d3100c0 100644
--- a/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/src/csharp/Grpc.Auth/packages.config b/src/csharp/Grpc.Auth/packages.config
index c20d9ce..11c6375 100644
--- a/src/csharp/Grpc.Auth/packages.config
+++ b/src/csharp/Grpc.Auth/packages.config
@@ -1,7 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
-  <package id="Google.Apis.Auth" version="1.11.1" targetFramework="net45" />
-  <package id="Google.Apis.Core" version="1.11.1" targetFramework="net45" />
+  <package id="Google.Apis" version="1.16.0" targetFramework="net45" />
+  <package id="Google.Apis.Auth" version="1.16.0" targetFramework="net45" />
+  <package id="Google.Apis.Core" version="1.16.0" targetFramework="net45" />
+  <package id="log4net" version="2.0.3" targetFramework="net45" />
   <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
+  <package id="Zlib.Portable.Signed" version="1.11.0" targetFramework="net45" />
 </packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Auth/project.json b/src/csharp/Grpc.Auth/project.json
index 08429f1..595e29f 100644
--- a/src/csharp/Grpc.Auth/project.json
+++ b/src/csharp/Grpc.Auth/project.json
@@ -23,18 +23,13 @@
   },
   "dependencies": {
     "Grpc.Core": "1.1.0-dev",
-    "Google.Apis.Auth": "1.11.1"
+    "Google.Apis.Auth": "1.16.0"
   },
   "frameworks": {
     "net45": { },
     "netstandard1.5": {
-      "imports": [
-        "net45"
-      ],
       "dependencies": {
-        "Microsoft.NETCore.Portable.Compatibility": "1.0.1-rc2-24027",
-        "NETStandard.Library": "1.5.0-rc2-24027",
-        "System.Threading.Tasks": "4.0.11-rc2-24027"
+        "NETStandard.Library": "1.6.0"
       }
     }
   }
diff --git a/src/csharp/Grpc.Core.Tests/AppDomainUnloadTest.cs b/src/csharp/Grpc.Core.Tests/AppDomainUnloadTest.cs
index 064bc13..d7ebdb4 100644
--- a/src/csharp/Grpc.Core.Tests/AppDomainUnloadTest.cs
+++ b/src/csharp/Grpc.Core.Tests/AppDomainUnloadTest.cs
@@ -40,7 +40,7 @@
 {
     public class AppDomainUnloadTest
     {
-#if NETSTANDARD1_5
+#if NETCOREAPP1_0
         [Test]
         [Ignore("Not supported for CoreCLR")]
         public void AppDomainUnloadHookCanCleanupAbandonedCall()
diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
index f6c2265..d99bf8e 100644
--- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
+++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
@@ -40,7 +40,7 @@
       <HintPath>..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll</HintPath>
     </Reference>
     <Reference Include="System.Interactive.Async">
-      <HintPath>..\packages\Ix-Async.1.2.5\lib\net45\System.Interactive.Async.dll</HintPath>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
     </Reference>
     <Reference Include="nunitlite">
       <HintPath>..\packages\NUnitLite.3.2.0\lib\net45\nunitlite.dll</HintPath>
@@ -108,4 +108,4 @@
     <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
   </ItemGroup>
   <ItemGroup />
-</Project>
\ No newline at end of file
+</Project>
diff --git a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs
index c35aaf6..0979012 100644
--- a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs
+++ b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs
@@ -33,6 +33,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Runtime.InteropServices;
 using System.Threading.Tasks;
 
@@ -149,8 +150,7 @@
 
             var writeTask = responseStream.WriteAsync("request1");
             fakeCall.SendCompletionHandler(false);
-            // TODO(jtattermusch): should we throw a different exception type instead?
-            Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await writeTask);
+            Assert.ThrowsAsync(typeof(IOException), async () => await writeTask);
 
             fakeCall.ReceivedCloseOnServerHandler(true, cancelled: true);
             AssertFinished(asyncCallServer, fakeCall, finishedTask);
diff --git a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
index 98e27a1..616bc06 100644
--- a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
+++ b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
@@ -180,21 +180,74 @@
         }
 
         [Test]
-        public void ClientStreaming_WriteCompletionFailure()
+        public void ClientStreaming_WriteFailureThrowsRpcException()
         {
             var resultTask = asyncCall.ClientStreamingCallAsync();
             var requestStream = new ClientRequestStream<string, string>(asyncCall);
 
             var writeTask = requestStream.WriteAsync("request1");
             fakeCall.SendCompletionHandler(false);
-            // TODO: maybe IOException or waiting for RPCException is more appropriate here.
-            Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await writeTask);
+
+            // The write will wait for call to finish to receive the status code.
+            Assert.IsFalse(writeTask.IsCompleted);
 
             fakeCall.UnaryResponseClientHandler(true,
                 CreateClientSideStatus(StatusCode.Internal),
                 null,
                 new Metadata());
 
+            var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
+            Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode);
+
+            AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.Internal);
+        }
+
+        [Test]
+        public void ClientStreaming_WriteFailureThrowsRpcException2()
+        {
+            var resultTask = asyncCall.ClientStreamingCallAsync();
+            var requestStream = new ClientRequestStream<string, string>(asyncCall);
+
+            var writeTask = requestStream.WriteAsync("request1");
+
+            fakeCall.UnaryResponseClientHandler(true,
+                CreateClientSideStatus(StatusCode.Internal),
+                null,
+                new Metadata());
+
+            fakeCall.SendCompletionHandler(false);
+
+            var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
+            Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode);
+
+            AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.Internal);
+        }
+
+        [Test]
+        public void ClientStreaming_WriteFailureThrowsRpcException3()
+        {
+            var resultTask = asyncCall.ClientStreamingCallAsync();
+            var requestStream = new ClientRequestStream<string, string>(asyncCall);
+
+            var writeTask = requestStream.WriteAsync("request1");
+            fakeCall.SendCompletionHandler(false);
+
+            // Until the delayed write completion has been triggered,
+            // we still act as if there was an active write.
+            Assert.Throws(typeof(InvalidOperationException), () => requestStream.WriteAsync("request2"));
+
+            fakeCall.UnaryResponseClientHandler(true,
+                CreateClientSideStatus(StatusCode.Internal),
+                null,
+                new Metadata());
+
+            var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
+            Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode);
+
+            // Following attempts to write keep delivering the same status
+            var ex2 = Assert.ThrowsAsync<RpcException>(async () => await requestStream.WriteAsync("after call has finished"));
+            Assert.AreEqual(StatusCode.Internal, ex2.Status.StatusCode);
+
             AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.Internal);
         }
 
@@ -416,6 +469,49 @@
         }
 
         [Test]
+        public void DuplexStreaming_WriteFailureThrowsRpcException()
+        {
+            asyncCall.StartDuplexStreamingCall();
+            var requestStream = new ClientRequestStream<string, string>(asyncCall);
+            var responseStream = new ClientResponseStream<string, string>(asyncCall);
+
+            var writeTask = requestStream.WriteAsync("request1");
+            fakeCall.SendCompletionHandler(false);
+
+            // The write will wait for call to finish to receive the status code.
+            Assert.IsFalse(writeTask.IsCompleted);
+
+            var readTask = responseStream.MoveNext();
+            fakeCall.ReceivedMessageHandler(true, null);
+            fakeCall.ReceivedStatusOnClientHandler(true, CreateClientSideStatus(StatusCode.PermissionDenied));
+
+            var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
+            Assert.AreEqual(StatusCode.PermissionDenied, ex.Status.StatusCode);
+
+            AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.PermissionDenied);
+        }
+
+        [Test]
+        public void DuplexStreaming_WriteFailureThrowsRpcException2()
+        {
+            asyncCall.StartDuplexStreamingCall();
+            var requestStream = new ClientRequestStream<string, string>(asyncCall);
+            var responseStream = new ClientResponseStream<string, string>(asyncCall);
+
+            var writeTask = requestStream.WriteAsync("request1");
+
+            var readTask = responseStream.MoveNext();
+            fakeCall.ReceivedMessageHandler(true, null);
+            fakeCall.ReceivedStatusOnClientHandler(true, CreateClientSideStatus(StatusCode.PermissionDenied));
+            fakeCall.SendCompletionHandler(false);
+
+            var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
+            Assert.AreEqual(StatusCode.PermissionDenied, ex.Status.StatusCode);
+
+            AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.PermissionDenied);
+        }
+
+        [Test]
         public void DuplexStreaming_WriteAfterCancellationRequestThrowsTaskCanceledException()
         {
             asyncCall.StartDuplexStreamingCall();
diff --git a/src/csharp/Grpc.Core.Tests/NUnitMain.cs b/src/csharp/Grpc.Core.Tests/NUnitMain.cs
index 24a9f84..870c726 100644
--- a/src/csharp/Grpc.Core.Tests/NUnitMain.cs
+++ b/src/csharp/Grpc.Core.Tests/NUnitMain.cs
@@ -49,7 +49,7 @@
         {
             // Make logger immune to NUnit capturing stdout and stderr to workaround https://github.com/nunit/nunit/issues/1406.
             GrpcEnvironment.SetLogger(new TextWriterLogger(Console.Error));
-#if NETSTANDARD1_5
+#if NETCOREAPP1_0
             return new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args, new ExtendedTextWrapper(Console.Out), Console.In);
 #else
             return new AutoRun().Execute(args);
diff --git a/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs
index c2e5e81..21808ae 100644
--- a/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/src/csharp/Grpc.Core.Tests/SanityTest.cs b/src/csharp/Grpc.Core.Tests/SanityTest.cs
index 9d069fa..f1eb13d 100644
--- a/src/csharp/Grpc.Core.Tests/SanityTest.cs
+++ b/src/csharp/Grpc.Core.Tests/SanityTest.cs
@@ -46,7 +46,7 @@
     public class SanityTest
     {
         // TODO: make sanity test work for CoreCLR as well
-#if !NETSTANDARD1_5
+#if !NETCOREAPP1_0
         /// <summary>
         /// Because we depend on a native library, sometimes when things go wrong, the
         /// entire NUnit test process crashes. To be able to track down problems better,
@@ -58,9 +58,9 @@
         [Test]
         public void TestsJsonUpToDate()
         {
-            Dictionary<string, List<string>> discoveredTests = DiscoverAllTestClasses();
-			Dictionary<string, List<string>> testsFromFile 
-			    = JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(ReadTestsJson());
+            var discoveredTests = DiscoverAllTestClasses();
+            var testsFromFile 
+                = JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(ReadTestsJson());
 
             Assert.AreEqual(discoveredTests, testsFromFile);
         }
diff --git a/src/csharp/Grpc.Core.Tests/packages.config b/src/csharp/Grpc.Core.Tests/packages.config
index 6a930c1..456ffcd 100644
--- a/src/csharp/Grpc.Core.Tests/packages.config
+++ b/src/csharp/Grpc.Core.Tests/packages.config
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Ix-Async" version="1.2.5" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
   <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
   <package id="NUnit" version="3.2.0" targetFramework="net45" />
   <package id="NUnitLite" version="3.2.0" targetFramework="net45" />
diff --git a/src/csharp/Grpc.Core.Tests/project.json b/src/csharp/Grpc.Core.Tests/project.json
index 4a682d9..faf28dc 100644
--- a/src/csharp/Grpc.Core.Tests/project.json
+++ b/src/csharp/Grpc.Core.Tests/project.json
@@ -42,11 +42,6 @@
       }
     }
   },
-  "runtimes": {
-    "win7-x64": { },
-    "debian.8-x64": { },
-    "osx.10.11-x64": { }
-  },
 
   "dependencies": {
     "Grpc.Core": {
@@ -61,12 +56,15 @@
   },
   "frameworks": {
     "net45": { },
-    "netstandard1.5": {
+    "netcoreapp1.0": {
       "imports": [
         "portable-net45"
       ],
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027"
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        }
       }
     }
   },
diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj
index 1952ee3..622813f 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.csproj
+++ b/src/csharp/Grpc.Core/Grpc.Core.csproj
@@ -40,7 +40,7 @@
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Interactive.Async">
-      <HintPath>..\packages\Ix-Async.1.2.5\lib\net45\System.Interactive.Async.dll</HintPath>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
     </Reference>
   </ItemGroup>
   <ItemGroup>
@@ -152,4 +152,4 @@
       <Link>roots.pem</Link>
     </EmbeddedResource>
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/src/csharp/Grpc.Core/Grpc.Core.nuspec b/src/csharp/Grpc.Core/Grpc.Core.nuspec
index 543549e..a8459c4 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.nuspec
+++ b/src/csharp/Grpc.Core/Grpc.Core.nuspec
@@ -15,7 +15,7 @@
     <copyright>Copyright 2015, Google Inc.</copyright>
     <tags>gRPC RPC Protocol HTTP/2</tags>
     <dependencies>
-      <dependency id="Ix-Async" version="1.2.5" />
+      <dependency id="System.Interactive.Async" version="3.0.0" />
     </dependencies>
   </metadata>
   <files>
diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs
index 574e900..3ed2df2 100644
--- a/src/csharp/Grpc.Core/GrpcEnvironment.cs
+++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs
@@ -351,11 +351,11 @@
                 {
                     if (!hooksRegistered)
                     {
-                        // TODO(jtattermusch): register shutdownhooks for CoreCLR as well
-#if !NETSTANDARD1_5
-
-                        AppDomain.CurrentDomain.ProcessExit += ShutdownHookHandler;
-                        AppDomain.CurrentDomain.DomainUnload += ShutdownHookHandler;
+#if NETSTANDARD1_5
+                        System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += (assemblyLoadContext) => { HandleShutdown(); };
+#else
+                        AppDomain.CurrentDomain.ProcessExit += (sender, eventArgs) => { HandleShutdown(); };
+                        AppDomain.CurrentDomain.DomainUnload += (sender, eventArgs) => { HandleShutdown(); };
 #endif
                     }
                     hooksRegistered = true;
@@ -363,9 +363,9 @@
             }
 
             /// <summary>
-            /// Handler for AppDomain.DomainUnload and AppDomain.ProcessExit hooks.
+            /// Handler for AppDomain.DomainUnload, AppDomain.ProcessExit and AssemblyLoadContext.Unloading hooks.
             /// </summary>
-            private static void ShutdownHookHandler(object sender, EventArgs e)
+            private static void HandleShutdown()
             {
                 Task.WaitAll(GrpcEnvironment.ShutdownChannelsAsync(), GrpcEnvironment.KillServersAsync());
             }
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index f549c52..9abaf11 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -341,6 +341,11 @@
             get { return true; }
         }
 
+        protected override Exception GetRpcExceptionClientOnly()
+        {
+            return new RpcException(finishedStatus.Value.Status);
+        }
+
         protected override Task CheckSendAllowedOrEarlyResult()
         {
             var earlyResult = CheckSendPreconditionsClientSide();
@@ -452,6 +457,7 @@
 
             using (Profilers.ForCurrentThread().NewScope("AsyncCall.HandleUnaryResponse"))
             {
+                TaskCompletionSource<object> delayedStreamingWriteTcs = null;
                 TResponse msg = default(TResponse);
                 var deserializeException = TryDeserialize(receivedMessage, out msg);
 
@@ -465,13 +471,23 @@
                     }
                     finishedStatus = receivedStatus;
 
+                    if (isStreamingWriteCompletionDelayed)
+                    {
+                        delayedStreamingWriteTcs = streamingWriteTcs;
+                        streamingWriteTcs = null;
+                    }
+
                     ReleaseResourcesIfPossible();
                 }
 
                 responseHeadersTcs.SetResult(responseHeaders);
 
-                var status = receivedStatus.Status;
+                if (delayedStreamingWriteTcs != null)
+                {
+                    delayedStreamingWriteTcs.SetException(GetRpcExceptionClientOnly());
+                }
 
+                var status = receivedStatus.Status;
                 if (status.StatusCode != StatusCode.OK)
                 {
                     unaryResponseTcs.SetException(new RpcException(status));
@@ -490,16 +506,27 @@
             // NOTE: because this event is a result of batch containing GRPC_OP_RECV_STATUS_ON_CLIENT,
             // success will be always set to true.
 
+            TaskCompletionSource<object> delayedStreamingWriteTcs = null;
+
             lock (myLock)
             {
                 finished = true;
                 finishedStatus = receivedStatus;
+                if (isStreamingWriteCompletionDelayed)
+                {
+                    delayedStreamingWriteTcs = streamingWriteTcs;
+                    streamingWriteTcs = null;
+                }
 
                 ReleaseResourcesIfPossible();
             }
 
-            var status = receivedStatus.Status;
+            if (delayedStreamingWriteTcs != null)
+            {
+                delayedStreamingWriteTcs.SetException(GetRpcExceptionClientOnly());
+            }
 
+            var status = receivedStatus.Status;
             if (status.StatusCode != StatusCode.OK)
             {
                 streamingCallFinishedTcs.SetException(new RpcException(status));
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
index eb9c3ea..9f9d260 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
@@ -69,6 +69,7 @@
         protected TaskCompletionSource<TRead> streamingReadTcs;  // Completion of a pending streaming read if not null.
         protected TaskCompletionSource<object> streamingWriteTcs;  // Completion of a pending streaming write or send close from client if not null.
         protected TaskCompletionSource<object> sendStatusFromServerTcs;
+        protected bool isStreamingWriteCompletionDelayed;  // Only used for the client side.
 
         protected bool readingDone;  // True if last read (i.e. read with null payload) was already received.
         protected bool halfcloseRequested;  // True if send close have been initiated.
@@ -200,6 +201,12 @@
             get;
         }
 
+        /// <summary>
+        /// Returns an exception to throw for a failed send operation.
+        /// It is only allowed to call this method for a call that has already finished.
+        /// </summary>
+        protected abstract Exception GetRpcExceptionClientOnly();
+
         private void ReleaseResources()
         {
             if (call != null)
@@ -252,18 +259,43 @@
         /// </summary>
         protected void HandleSendFinished(bool success)
         {
+            bool delayCompletion = false;
             TaskCompletionSource<object> origTcs = null;
             lock (myLock)
             {
-                origTcs = streamingWriteTcs;
-                streamingWriteTcs = null;
+                if (!success && !finished && IsClient) {
+                    // We should be setting this only once per call, following writes will be short circuited
+                    // because they cannot start until the entire call finishes.
+                    GrpcPreconditions.CheckState(!isStreamingWriteCompletionDelayed);
+
+                    // leave streamingWriteTcs set, it will be completed once call finished.
+                    isStreamingWriteCompletionDelayed = true;
+                    delayCompletion = true;
+                }
+                else
+                {
+                    origTcs = streamingWriteTcs;
+                    streamingWriteTcs = null;    
+                }
 
                 ReleaseResourcesIfPossible();
             }
 
             if (!success)
             {
-                origTcs.SetException(new InvalidOperationException("Send failed"));
+                if (!delayCompletion)
+                {
+                    if (IsClient)
+                    {
+                        GrpcPreconditions.CheckState(finished);  // implied by !success && !delayCompletion && IsClient
+                        origTcs.SetException(GetRpcExceptionClientOnly());
+                    }
+                    else
+                    {
+                        origTcs.SetException (new IOException("Error sending from server."));
+                    }
+                }
+                // if delayCompletion == true, postpone SetException until call finishes.
             }
             else
             {
@@ -283,7 +315,7 @@
 
             if (!success)
             {
-                sendStatusFromServerTcs.SetException(new InvalidOperationException("Error sending status from server."));
+                sendStatusFromServerTcs.SetException(new IOException("Error sending status from server."));
             }
             else
             {
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
index 56c23ba..50fdfa9 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
@@ -33,6 +33,7 @@
 
 using System;
 using System.Diagnostics;
+using System.IO;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Threading;
@@ -193,6 +194,11 @@
             get { return false; }
         }
 
+        protected override Exception GetRpcExceptionClientOnly()
+        {
+            throw new InvalidOperationException("Call be only called for client calls");
+        }
+
         protected override void OnAfterReleaseResources()
         {
             server.RemoveCallReference(this);
diff --git a/src/csharp/Grpc.Core/Internal/NativeExtension.cs b/src/csharp/Grpc.Core/Internal/NativeExtension.cs
index 509baf7..61d8b57 100644
--- a/src/csharp/Grpc.Core/Internal/NativeExtension.cs
+++ b/src/csharp/Grpc.Core/Internal/NativeExtension.cs
@@ -100,19 +100,19 @@
 
             // With old-style VS projects, the native libraries get copied using a .targets rule to the build output folder
             // alongside the compiled assembly.
-            // With dotnet cli projects, the native libraries (just the required ones) are similarly copied to the built output folder,
-            // through the magic of Microsoft.NETCore.Platforms.
+            // With dotnet cli projects targeting net45 framework, the native libraries (just the required ones)
+            // are similarly copied to the built output folder, through the magic of Microsoft.NETCore.Platforms.
             var classicPath = Path.Combine(assemblyDirectory, GetNativeLibraryFilename());
 
-            // DNX-style project.json projects will use Grpc.Core assembly directly in the location where it got restored
+            // With dotnet cli project targeting netcoreapp1.0, projects will use Grpc.Core assembly directly in the location where it got restored
             // by nuget. We locate the native libraries based on known structure of Grpc.Core nuget package.
+            // When "dotnet publish" is used, the runtimes directory is copied next to the published assemblies.
+            string runtimesDirectory = string.Format("runtimes/{0}/native", GetPlatformString());
+            var netCorePublishedAppStylePath = Path.Combine(assemblyDirectory, runtimesDirectory, GetNativeLibraryFilename());
+            var netCoreAppStylePath = Path.Combine(assemblyDirectory, "../..", runtimesDirectory, GetNativeLibraryFilename());
 
-            // TODO: Support .NET Core applications, which act slightly differently. We may be okay if "dotnet publish"
-            // is used, but "dotnet run" leaves the native libraries in-package, while copying assemblies.
-            string platform = GetPlatformString();
-            string relativeDirectory = string.Format("../../runtimes/{0}/native", platform);
-            var dnxStylePath = Path.Combine(assemblyDirectory, relativeDirectory, GetNativeLibraryFilename());
-            string[] paths = new[] { classicPath, dnxStylePath };
+            // Look for all native library in all possible locations in given order.
+            string[] paths = new[] { classicPath, netCorePublishedAppStylePath, netCoreAppStylePath};
             return new UnmanagedLibrary(paths);
         }
 
diff --git a/src/csharp/Grpc.Core/Internal/PlatformApis.cs b/src/csharp/Grpc.Core/Internal/PlatformApis.cs
index 15391dd..406204e 100644
--- a/src/csharp/Grpc.Core/Internal/PlatformApis.cs
+++ b/src/csharp/Grpc.Core/Internal/PlatformApis.cs
@@ -50,6 +50,7 @@
         static readonly bool isMacOSX;
         static readonly bool isWindows;
         static readonly bool isMono;
+        static readonly bool isNetCore;
 
         static PlatformApis()
         {
@@ -57,6 +58,7 @@
             isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
             isMacOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
             isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+            isNetCore = RuntimeInformation.FrameworkDescription.StartsWith(".NET Core");
 #else
             var platform = Environment.OSVersion.Platform;
 
@@ -64,6 +66,7 @@
             isMacOSX = (platform == PlatformID.Unix && GetUname() == "Darwin");
             isLinux = (platform == PlatformID.Unix && !isMacOSX);
             isWindows = (platform == PlatformID.Win32NT || platform == PlatformID.Win32S || platform == PlatformID.Win32Windows);
+            isNetCore = false;
 #endif
             isMono = Type.GetType("Mono.Runtime") != null;
         }
@@ -88,6 +91,14 @@
             get { return isMono; }
         }
 
+        /// <summary>
+        /// true if running on .NET Core (CoreCLR), false otherwise.
+        /// </summary>
+        public static bool IsNetCore
+        {
+            get { return isNetCore; }
+        }
+
         public static bool Is64Bit
         {
             get { return IntPtr.Size == 8; }
diff --git a/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs b/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs
index dc629bd..31e1402 100644
--- a/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs
+++ b/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs
@@ -44,10 +44,9 @@
 {
     /// <summary>
     /// Represents a dynamically loaded unmanaged library in a (partially) platform independent manner.
-    /// An important difference in library loading semantics is that on Windows, once we load a dynamic library using LoadLibrary,
-    /// that library becomes instantly available for <c>DllImport</c> P/Invoke calls referring to the same library name.
-    /// On Unix systems, dlopen has somewhat different semantics, so we need to use dlsym and <c>Marshal.GetDelegateForFunctionPointer</c>
-    /// to obtain delegates to native methods.
+    /// First, the native library is loaded using dlopen (on Unix systems) or using LoadLibrary (on Windows).
+    /// dlsym or GetProcAddress are then used to obtain symbol addresses. <c>Marshal.GetDelegateForFunctionPointer</c>
+    /// transforms the addresses into delegates to native methods.
     /// See http://stackoverflow.com/questions/13461989/p-invoke-to-dynamically-loaded-library-on-mono.
     /// </summary>
     internal class UnmanagedLibrary
@@ -114,6 +113,10 @@
                 {
                     return Mono.dlsym(this.handle, symbolName);
                 }
+                if (PlatformApis.IsNetCore)
+                {
+                    return CoreCLR.dlsym(this.handle, symbolName);
+                }
                 return Linux.dlsym(this.handle, symbolName);
             }
             if (PlatformApis.IsMacOSX)
@@ -149,6 +152,10 @@
                 {
                     return Mono.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
                 }
+                if (PlatformApis.IsNetCore)
+                {
+                    return CoreCLR.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
+                }
                 return Linux.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
             }
             if (PlatformApis.IsMacOSX)
@@ -215,5 +222,19 @@
             [DllImport("__Internal")]
             internal static extern IntPtr dlsym(IntPtr handle, string symbol);
         }
+
+        /// <summary>
+        /// Similarly as for Mono on Linux, we load symbols for
+        /// dlopen and dlsym from the "libcoreclr.so",
+        /// to avoid the dependency on libc-dev Linux.
+        /// </summary>
+        private static class CoreCLR
+        {
+            [DllImport("libcoreclr.so")]
+            internal static extern IntPtr dlopen(string filename, int flags);
+
+            [DllImport("libcoreclr.so")]
+            internal static extern IntPtr dlsym(IntPtr handle, string symbol);
+        }
     }
 }
diff --git a/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs
index 370fa98..99158fb 100644
--- a/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/src/csharp/Grpc.Core/packages.config b/src/csharp/Grpc.Core/packages.config
index 80daf04..6514774 100644
--- a/src/csharp/Grpc.Core/packages.config
+++ b/src/csharp/Grpc.Core/packages.config
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Ix-Async" version="1.2.5" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
 </packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/project.json b/src/csharp/Grpc.Core/project.json
index 8075088..aa1e4cc 100644
--- a/src/csharp/Grpc.Core/project.json
+++ b/src/csharp/Grpc.Core/project.json
@@ -31,17 +31,15 @@
     "xmlDoc": true
   },
   "dependencies": {
-    "Ix-Async": "1.2.5"
+    "System.Interactive.Async": "3.0.0"
   },
   "frameworks": {
     "net45": { },
     "netstandard1.5": {
-      "imports": [
-        "portable-net45"
-      ],
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027",
-        "System.Threading.Thread": "4.0.0-rc2-24027"
+        "NETStandard.Library": "1.6.0",
+        "System.Runtime.Loader": "4.0.0",
+        "System.Threading.Thread": "4.0.0"
       }
     }
   }
diff --git a/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs
index 0fb0dbd..68265a9 100644
--- a/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/src/csharp/Grpc.Examples.MathClient/project.json b/src/csharp/Grpc.Examples.MathClient/project.json
index 206d6c5..628f532 100644
--- a/src/csharp/Grpc.Examples.MathClient/project.json
+++ b/src/csharp/Grpc.Examples.MathClient/project.json
@@ -42,11 +42,6 @@
       }
     }
   },
-  "runtimes": {
-    "win7-x64": { },
-    "debian.8-x64": { },
-    "osx.10.11-x64": { }
-  },
 
   "dependencies": {
     "Grpc.Examples": {
@@ -55,12 +50,12 @@
   },
   "frameworks": {
     "net45": { },
-    "netstandard1.5": {
-      "imports": [
-        "portable-net45"
-      ],
+    "netcoreapp1.0": {
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027"
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        }
       }
     }
   }
diff --git a/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs
index 63035b6..705aa77 100644
--- a/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/src/csharp/Grpc.Examples.MathServer/project.json b/src/csharp/Grpc.Examples.MathServer/project.json
index 206d6c5..628f532 100644
--- a/src/csharp/Grpc.Examples.MathServer/project.json
+++ b/src/csharp/Grpc.Examples.MathServer/project.json
@@ -42,11 +42,6 @@
       }
     }
   },
-  "runtimes": {
-    "win7-x64": { },
-    "debian.8-x64": { },
-    "osx.10.11-x64": { }
-  },
 
   "dependencies": {
     "Grpc.Examples": {
@@ -55,12 +50,12 @@
   },
   "frameworks": {
     "net45": { },
-    "netstandard1.5": {
-      "imports": [
-        "portable-net45"
-      ],
+    "netcoreapp1.0": {
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027"
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        }
       }
     }
   }
diff --git a/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj b/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj
index 4c7d893..a114d96 100644
--- a/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj
+++ b/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj
@@ -36,18 +36,18 @@
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
-    <Reference Include="Google.Protobuf">
-      <HintPath>..\packages\Google.Protobuf.3.0.0-beta3\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
-    </Reference>
     <Reference Include="nunit.framework">
       <HintPath>..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll</HintPath>
     </Reference>
     <Reference Include="System.Interactive.Async">
-      <HintPath>..\packages\Ix-Async.1.2.5\lib\net45\System.Interactive.Async.dll</HintPath>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
     </Reference>
     <Reference Include="nunitlite">
       <HintPath>..\packages\NUnitLite.3.2.0\lib\net45\nunitlite.dll</HintPath>
     </Reference>
+    <Reference Include="Google.Protobuf">
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\Grpc.Core\Version.cs">
@@ -75,4 +75,4 @@
   <ItemGroup>
     <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/src/csharp/Grpc.Examples.Tests/NUnitMain.cs b/src/csharp/Grpc.Examples.Tests/NUnitMain.cs
index 1a522ca..7ba1074 100644
--- a/src/csharp/Grpc.Examples.Tests/NUnitMain.cs
+++ b/src/csharp/Grpc.Examples.Tests/NUnitMain.cs
@@ -49,7 +49,7 @@
         {
             // Make logger immune to NUnit capturing stdout and stderr to workaround https://github.com/nunit/nunit/issues/1406.
             GrpcEnvironment.SetLogger(new TextWriterLogger(Console.Error));
-#if NETSTANDARD1_5
+#if NETCOREAPP1_0
             return new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args, new ExtendedTextWrapper(Console.Out), Console.In);
 #else
             return new AutoRun().Execute(args);
diff --git a/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs
index 846afb4..091d1e5 100644
--- a/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/src/csharp/Grpc.Examples.Tests/packages.config b/src/csharp/Grpc.Examples.Tests/packages.config
index 668601a..f14517d 100644
--- a/src/csharp/Grpc.Examples.Tests/packages.config
+++ b/src/csharp/Grpc.Examples.Tests/packages.config
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Google.Protobuf" version="3.0.0-beta3" targetFramework="net45" />
-  <package id="Ix-Async" version="1.2.5" targetFramework="net45" />
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
   <package id="NUnit" version="3.2.0" targetFramework="net45" />
   <package id="NUnitLite" version="3.2.0" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
 </packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Examples.Tests/project.json b/src/csharp/Grpc.Examples.Tests/project.json
index b4c4c5f..0109617 100644
--- a/src/csharp/Grpc.Examples.Tests/project.json
+++ b/src/csharp/Grpc.Examples.Tests/project.json
@@ -42,11 +42,6 @@
       }
     }
   },
-  "runtimes": {
-    "win7-x64": { },
-    "debian.8-x64": { },
-    "osx.10.11-x64": { }
-  },
 
   "dependencies": {
     "Grpc.Examples": {
@@ -57,12 +52,15 @@
   },
   "frameworks": {
     "net45": { },
-    "netstandard1.5": {
+    "netcoreapp1.0": {
       "imports": [
         "portable-net45"
       ],
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027"
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        }
       }
     }
   }
diff --git a/src/csharp/Grpc.Examples/Grpc.Examples.csproj b/src/csharp/Grpc.Examples/Grpc.Examples.csproj
index 3dfa84e..dcbef5fc 100644
--- a/src/csharp/Grpc.Examples/Grpc.Examples.csproj
+++ b/src/csharp/Grpc.Examples/Grpc.Examples.csproj
@@ -37,10 +37,6 @@
     <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="Google.Protobuf, Version=3.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Protobuf.3.0.0-beta3\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
-    </Reference>
     <Reference Include="nunit.framework">
       <HintPath>..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll</HintPath>
     </Reference>
@@ -48,7 +44,10 @@
     <Reference Include="System.Data.Linq" />
     <Reference Include="System.Interactive.Async, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Ix-Async.1.2.5\lib\net45\System.Interactive.Async.dll</HintPath>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Protobuf">
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
     </Reference>
   </ItemGroup>
   <ItemGroup>
@@ -72,4 +71,4 @@
     <None Include="Grpc.Examples.project.json" />
     <None Include="packages.config" />
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs
index 9211138..1eed603 100644
--- a/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/src/csharp/Grpc.Examples/packages.config b/src/csharp/Grpc.Examples/packages.config
index a70dcbd..d17347f 100644
--- a/src/csharp/Grpc.Examples/packages.config
+++ b/src/csharp/Grpc.Examples/packages.config
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Google.Protobuf" version="3.0.0-beta3" targetFramework="net45" />
-  <package id="Ix-Async" version="1.2.5" targetFramework="net45" />
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
   <package id="NUnit" version="3.2.0" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
 </packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Examples/project.json b/src/csharp/Grpc.Examples/project.json
index 7d3f4dc..21a730c 100644
--- a/src/csharp/Grpc.Examples/project.json
+++ b/src/csharp/Grpc.Examples/project.json
@@ -6,7 +6,7 @@
     "Grpc.Core": {
       "target": "project"
     },
-    "Google.Protobuf": "3.0.0-beta3"
+    "Google.Protobuf": "3.0.0"
   },
   "frameworks": {
     "net45": {
@@ -15,12 +15,12 @@
         "System.IO": ""
       }
     },
-    "netstandard1.5": {
-      "imports": [
-        "portable-net45"
-      ],
+    "netcoreapp1.0": {
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027"
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        }
       }
     }
   }
diff --git a/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj b/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj
index aefacfb..93c3b3a 100644
--- a/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj
+++ b/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj
@@ -44,15 +44,15 @@
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Data" />
     <Reference Include="System.Xml" />
-    <Reference Include="Google.Protobuf">
-      <HintPath>..\packages\Google.Protobuf.3.0.0-beta3\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
-    </Reference>
     <Reference Include="nunit.framework">
       <HintPath>..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll</HintPath>
     </Reference>
     <Reference Include="nunitlite">
       <HintPath>..\packages\NUnitLite.3.2.0\lib\net45\nunitlite.dll</HintPath>
     </Reference>
+    <Reference Include="Google.Protobuf">
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\Grpc.Core\Version.cs">
diff --git a/src/csharp/Grpc.HealthCheck.Tests/NUnitMain.cs b/src/csharp/Grpc.HealthCheck.Tests/NUnitMain.cs
index 4463467..dca61e3 100644
--- a/src/csharp/Grpc.HealthCheck.Tests/NUnitMain.cs
+++ b/src/csharp/Grpc.HealthCheck.Tests/NUnitMain.cs
@@ -49,7 +49,7 @@
         {
             // Make logger immune to NUnit capturing stdout and stderr to workaround https://github.com/nunit/nunit/issues/1406.
             GrpcEnvironment.SetLogger(new TextWriterLogger(Console.Error));
-#if NETSTANDARD1_5
+#if NETCOREAPP1_0
             return new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args, new ExtendedTextWrapper(Console.Out), Console.In);
 #else
             return new AutoRun().Execute(args);
diff --git a/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs
index d566030..5f15841 100644
--- a/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs
@@ -1,4 +1,37 @@
-using System.Reflection;
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System.Reflection;
 using System.Runtime.CompilerServices;
 
 [assembly: AssemblyTitle("Grpc.HealthCheck.Tests")]
diff --git a/src/csharp/Grpc.HealthCheck.Tests/packages.config b/src/csharp/Grpc.HealthCheck.Tests/packages.config
index 2bcfec8..e796d6b 100644
--- a/src/csharp/Grpc.HealthCheck.Tests/packages.config
+++ b/src/csharp/Grpc.HealthCheck.Tests/packages.config
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Google.Protobuf" version="3.0.0-beta3" targetFramework="net45" />
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
   <package id="NUnit" version="3.2.0" targetFramework="net45" />
   <package id="NUnitLite" version="3.2.0" targetFramework="net45" />
 </packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.HealthCheck.Tests/project.json b/src/csharp/Grpc.HealthCheck.Tests/project.json
index f44a322..66dcd79 100644
--- a/src/csharp/Grpc.HealthCheck.Tests/project.json
+++ b/src/csharp/Grpc.HealthCheck.Tests/project.json
@@ -42,11 +42,6 @@
       }
     }
   },
-  "runtimes": {
-    "win7-x64": { },
-    "debian.8-x64": { },
-    "osx.10.11-x64": { }
-  },
 
   "dependencies": {
     "Grpc.HealthCheck": {
@@ -57,12 +52,15 @@
   },
   "frameworks": {
     "net45": { },
-    "netstandard1.5": {
+    "netcoreapp1.0": {
       "imports": [
         "portable-net45"
       ],
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027"
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        }
       }
     }
   }
diff --git a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj
index 7db8b2d..54a2984 100644
--- a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj
+++ b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj
@@ -38,21 +38,20 @@
     <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="Google.Protobuf, Version=3.0.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Protobuf.3.0.0-beta3\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="System.Interactive.Async, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Ix-Async.1.2.5\lib\net45\System.Interactive.Async.dll</HintPath>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
     </Reference>
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Data" />
     <Reference Include="System.Xml" />
+    <Reference Include="Google.Protobuf">
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\Grpc.Core\Version.cs">
@@ -82,4 +81,4 @@
   <Target Name="AfterBuild">
   </Target>
   -->
-</Project>
\ No newline at end of file
+</Project>
diff --git a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec
index 7b3b391..4ffd18c 100644
--- a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec
+++ b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec
@@ -16,7 +16,7 @@
 	<dependencies>
 	  <dependency id="Google.Protobuf" version="$ProtobufVersion$" />
 	  <dependency id="Grpc.Core" version="$version$" />
-	  <dependency id="Ix-Async" version="1.2.3" />
+	  <dependency id="System.Interactive.Async" version="3.0.0" />
     </dependencies>
   </metadata>
   <files>
diff --git a/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs b/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs
index 4d7b33c..73a2c1f 100644
--- a/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs
@@ -1,4 +1,37 @@
-using System.Reflection;
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System.Reflection;
 using System.Runtime.CompilerServices;
 
 [assembly: AssemblyTitle("Grpc.HealthCheck")]
diff --git a/src/csharp/Grpc.HealthCheck/packages.config b/src/csharp/Grpc.HealthCheck/packages.config
index a52d9e5..f8e353b 100644
--- a/src/csharp/Grpc.HealthCheck/packages.config
+++ b/src/csharp/Grpc.HealthCheck/packages.config
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Google.Protobuf" version="3.0.0-beta3" targetFramework="net45" />
-  <package id="Ix-Async" version="1.2.5" targetFramework="net45" />
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
 </packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.HealthCheck/project.json b/src/csharp/Grpc.HealthCheck/project.json
index 0e03e89..9d63122 100644
--- a/src/csharp/Grpc.HealthCheck/project.json
+++ b/src/csharp/Grpc.HealthCheck/project.json
@@ -23,7 +23,7 @@
   },
   "dependencies": {
     "Grpc.Core": "1.1.0-dev",
-    "Google.Protobuf": "3.0.0-beta3"
+    "Google.Protobuf": "3.0.0"
   },
   "frameworks": {
     "net45": {
@@ -33,11 +33,8 @@
       }
     },
     "netstandard1.5": {
-      "imports": [
-        "portable-net45"
-      ],
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027"
+        "NETStandard.Library": "1.6.0"
       }
     }
   }
diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
index 91fb3ce..ae58073 100644
--- a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
+++ b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
@@ -39,30 +39,37 @@
     <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Auth, Version=1.11.1.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Apis.Auth.1.11.1\lib\net45\Google.Apis.Auth.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.11.1.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Apis.Auth.1.11.1\lib\net45\Google.Apis.Auth.PlatformServices.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Core, Version=1.11.1.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Apis.Core.1.11.1\lib\net45\Google.Apis.Core.dll</HintPath>
-    </Reference>
-    <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Net" />
     <Reference Include="System.Net.Http" />
     <Reference Include="System.Net.Http.WebRequest" />
+    <Reference Include="BouncyCastle.Crypto">
+      <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json">
+      <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="log4net">
+      <HintPath>..\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Core">
+      <HintPath>..\packages\Google.Apis.Core.1.16.0\lib\net45\Google.Apis.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="Zlib.Portable">
+      <HintPath>..\packages\Zlib.Portable.Signed.1.11.0\lib\portable-net4+sl5+wp8+win8+wpa81+MonoTouch+MonoAndroid\Zlib.Portable.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis">
+      <HintPath>..\packages\Google.Apis.1.16.0\lib\net45\Google.Apis.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.PlatformServices">
+      <HintPath>..\packages\Google.Apis.1.16.0\lib\net45\Google.Apis.PlatformServices.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth">
+      <HintPath>..\packages\Google.Apis.Auth.1.16.0\lib\net45\Google.Apis.Auth.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth.PlatformServices">
+      <HintPath>..\packages\Google.Apis.Auth.1.16.0\lib\net45\Google.Apis.Auth.PlatformServices.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\Grpc.Core\Version.cs">
diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs
index f51f279..bb1c698 100644
--- a/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/src/csharp/Grpc.IntegrationTesting.Client/packages.config b/src/csharp/Grpc.IntegrationTesting.Client/packages.config
index c20d9ce..11c6375 100644
--- a/src/csharp/Grpc.IntegrationTesting.Client/packages.config
+++ b/src/csharp/Grpc.IntegrationTesting.Client/packages.config
@@ -1,7 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
-  <package id="Google.Apis.Auth" version="1.11.1" targetFramework="net45" />
-  <package id="Google.Apis.Core" version="1.11.1" targetFramework="net45" />
+  <package id="Google.Apis" version="1.16.0" targetFramework="net45" />
+  <package id="Google.Apis.Auth" version="1.16.0" targetFramework="net45" />
+  <package id="Google.Apis.Core" version="1.16.0" targetFramework="net45" />
+  <package id="log4net" version="2.0.3" targetFramework="net45" />
   <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
+  <package id="Zlib.Portable.Signed" version="1.11.0" targetFramework="net45" />
 </packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.IntegrationTesting.Client/project.json b/src/csharp/Grpc.IntegrationTesting.Client/project.json
index 6b61a4b..1b900c8 100644
--- a/src/csharp/Grpc.IntegrationTesting.Client/project.json
+++ b/src/csharp/Grpc.IntegrationTesting.Client/project.json
@@ -13,8 +13,10 @@
           "includeFiles": [ "../Grpc.Core/Version.cs" ]
         },
         "copyToOutput": {
-          "include": "data/*",
           "mappings": {
+            "data/ca.pem": "../Grpc.IntegrationTesting/data/ca.pem",
+            "data/server1.key": "../Grpc.IntegrationTesting/data/server1.key",
+            "data/server1.pem": "../Grpc.IntegrationTesting/data/server1.pem",
             "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Debug/grpc_csharp_ext.dll",
             "grpc_csharp_ext.x86.dll": "../../../vsprojects/Debug/grpc_csharp_ext.dll",
             "libgrpc_csharp_ext.x64.so": "../../../libs/dbg/libgrpc_csharp_ext.so",
@@ -33,8 +35,10 @@
           "includeFiles": [ "../Grpc.Core/Version.cs" ]
         },
         "copyToOutput": {
-          "include": "data/*",
           "mappings": {
+            "data/ca.pem": "../Grpc.IntegrationTesting/data/ca.pem",
+            "data/server1.key": "../Grpc.IntegrationTesting/data/server1.key",
+            "data/server1.pem": "../Grpc.IntegrationTesting/data/server1.pem",
             "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Release/grpc_csharp_ext.dll",
             "grpc_csharp_ext.x86.dll": "../../../vsprojects/Release/grpc_csharp_ext.dll",
             "libgrpc_csharp_ext.x64.so": "../../../libs/opt/libgrpc_csharp_ext.so",
@@ -44,11 +48,6 @@
       }
     }
   },
-  "runtimes": {
-    "win7-x64": { },
-    "debian.8-x64": { },
-    "osx.10.11-x64": { }
-  },
 
   "dependencies": {
     "Grpc.IntegrationTesting": {
@@ -57,13 +56,15 @@
   },
   "frameworks": {
     "net45": { },
-    "netstandard1.5": {
+    "netcoreapp1.0": {
       "imports": [
-        "portable-net45",
-        "net45"
+        "portable-net45"
       ],
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027"
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        }
       }
     }
   }
diff --git a/src/csharp/Grpc.IntegrationTesting.QpsWorker/Grpc.IntegrationTesting.QpsWorker.csproj b/src/csharp/Grpc.IntegrationTesting.QpsWorker/Grpc.IntegrationTesting.QpsWorker.csproj
index dda26a6..593bf09 100644
--- a/src/csharp/Grpc.IntegrationTesting.QpsWorker/Grpc.IntegrationTesting.QpsWorker.csproj
+++ b/src/csharp/Grpc.IntegrationTesting.QpsWorker/Grpc.IntegrationTesting.QpsWorker.csproj
@@ -58,7 +58,6 @@
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>
-    <None Include="app.config" />
     <None Include="Grpc.IntegrationTesting.QpsWorker.project.json" />
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.IntegrationTesting.QpsWorker/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.QpsWorker/Properties/AssemblyInfo.cs
index aacfc16..f05d18e 100644
--- a/src/csharp/Grpc.IntegrationTesting.QpsWorker/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.IntegrationTesting.QpsWorker/Properties/AssemblyInfo.cs
@@ -1,4 +1,37 @@
-using System.Reflection;
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System.Reflection;
 using System.Runtime.CompilerServices;
 
 [assembly: AssemblyTitle("Grpc.IntegrationTesting.QpsWorker")]
diff --git a/src/csharp/Grpc.IntegrationTesting.QpsWorker/app.config b/src/csharp/Grpc.IntegrationTesting.QpsWorker/app.config
deleted file mode 100644
index e204447..0000000
--- a/src/csharp/Grpc.IntegrationTesting.QpsWorker/app.config
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
-  <runtime>
-    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
-      <dependentAssembly>
-        <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
-        <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" />
-      </dependentAssembly>
-      <dependentAssembly>
-        <assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" />
-        <bindingRedirect oldVersion="0.0.0.0-1.11.1.0" newVersion="1.11.1.0" />
-      </dependentAssembly>
-    </assemblyBinding>
-  </runtime>
-</configuration>
\ No newline at end of file
diff --git a/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json b/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json
index 6b61a4b..1b900c8 100644
--- a/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json
+++ b/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json
@@ -13,8 +13,10 @@
           "includeFiles": [ "../Grpc.Core/Version.cs" ]
         },
         "copyToOutput": {
-          "include": "data/*",
           "mappings": {
+            "data/ca.pem": "../Grpc.IntegrationTesting/data/ca.pem",
+            "data/server1.key": "../Grpc.IntegrationTesting/data/server1.key",
+            "data/server1.pem": "../Grpc.IntegrationTesting/data/server1.pem",
             "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Debug/grpc_csharp_ext.dll",
             "grpc_csharp_ext.x86.dll": "../../../vsprojects/Debug/grpc_csharp_ext.dll",
             "libgrpc_csharp_ext.x64.so": "../../../libs/dbg/libgrpc_csharp_ext.so",
@@ -33,8 +35,10 @@
           "includeFiles": [ "../Grpc.Core/Version.cs" ]
         },
         "copyToOutput": {
-          "include": "data/*",
           "mappings": {
+            "data/ca.pem": "../Grpc.IntegrationTesting/data/ca.pem",
+            "data/server1.key": "../Grpc.IntegrationTesting/data/server1.key",
+            "data/server1.pem": "../Grpc.IntegrationTesting/data/server1.pem",
             "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Release/grpc_csharp_ext.dll",
             "grpc_csharp_ext.x86.dll": "../../../vsprojects/Release/grpc_csharp_ext.dll",
             "libgrpc_csharp_ext.x64.so": "../../../libs/opt/libgrpc_csharp_ext.so",
@@ -44,11 +48,6 @@
       }
     }
   },
-  "runtimes": {
-    "win7-x64": { },
-    "debian.8-x64": { },
-    "osx.10.11-x64": { }
-  },
 
   "dependencies": {
     "Grpc.IntegrationTesting": {
@@ -57,13 +56,15 @@
   },
   "frameworks": {
     "net45": { },
-    "netstandard1.5": {
+    "netcoreapp1.0": {
       "imports": [
-        "portable-net45",
-        "net45"
+        "portable-net45"
       ],
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027"
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        }
       }
     }
   }
diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
index f73d99d..d5c40ba 100644
--- a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
+++ b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
@@ -39,30 +39,37 @@
     <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Auth, Version=1.11.1.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Apis.Auth.1.11.1\lib\net45\Google.Apis.Auth.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.11.1.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Apis.Auth.1.11.1\lib\net45\Google.Apis.Auth.PlatformServices.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Core, Version=1.11.1.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Google.Apis.Core.1.11.1\lib\net45\Google.Apis.Core.dll</HintPath>
-    </Reference>
-    <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Net" />
     <Reference Include="System.Net.Http" />
     <Reference Include="System.Net.Http.WebRequest" />
+    <Reference Include="BouncyCastle.Crypto">
+      <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json">
+      <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="log4net">
+      <HintPath>..\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Core">
+      <HintPath>..\packages\Google.Apis.Core.1.16.0\lib\net45\Google.Apis.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="Zlib.Portable">
+      <HintPath>..\packages\Zlib.Portable.Signed.1.11.0\lib\portable-net4+sl5+wp8+win8+wpa81+MonoTouch+MonoAndroid\Zlib.Portable.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis">
+      <HintPath>..\packages\Google.Apis.1.16.0\lib\net45\Google.Apis.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.PlatformServices">
+      <HintPath>..\packages\Google.Apis.1.16.0\lib\net45\Google.Apis.PlatformServices.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth">
+      <HintPath>..\packages\Google.Apis.Auth.1.16.0\lib\net45\Google.Apis.Auth.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth.PlatformServices">
+      <HintPath>..\packages\Google.Apis.Auth.1.16.0\lib\net45\Google.Apis.Auth.PlatformServices.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\Grpc.Core\Version.cs">
diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs
index f68d9a3..7ed6931 100644
--- a/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/src/csharp/Grpc.IntegrationTesting.Server/packages.config b/src/csharp/Grpc.IntegrationTesting.Server/packages.config
index c20d9ce..11c6375 100644
--- a/src/csharp/Grpc.IntegrationTesting.Server/packages.config
+++ b/src/csharp/Grpc.IntegrationTesting.Server/packages.config
@@ -1,7 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
-  <package id="Google.Apis.Auth" version="1.11.1" targetFramework="net45" />
-  <package id="Google.Apis.Core" version="1.11.1" targetFramework="net45" />
+  <package id="Google.Apis" version="1.16.0" targetFramework="net45" />
+  <package id="Google.Apis.Auth" version="1.16.0" targetFramework="net45" />
+  <package id="Google.Apis.Core" version="1.16.0" targetFramework="net45" />
+  <package id="log4net" version="2.0.3" targetFramework="net45" />
   <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
+  <package id="Zlib.Portable.Signed" version="1.11.0" targetFramework="net45" />
 </packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.IntegrationTesting.Server/project.json b/src/csharp/Grpc.IntegrationTesting.Server/project.json
index 6b61a4b..1b900c8 100644
--- a/src/csharp/Grpc.IntegrationTesting.Server/project.json
+++ b/src/csharp/Grpc.IntegrationTesting.Server/project.json
@@ -13,8 +13,10 @@
           "includeFiles": [ "../Grpc.Core/Version.cs" ]
         },
         "copyToOutput": {
-          "include": "data/*",
           "mappings": {
+            "data/ca.pem": "../Grpc.IntegrationTesting/data/ca.pem",
+            "data/server1.key": "../Grpc.IntegrationTesting/data/server1.key",
+            "data/server1.pem": "../Grpc.IntegrationTesting/data/server1.pem",
             "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Debug/grpc_csharp_ext.dll",
             "grpc_csharp_ext.x86.dll": "../../../vsprojects/Debug/grpc_csharp_ext.dll",
             "libgrpc_csharp_ext.x64.so": "../../../libs/dbg/libgrpc_csharp_ext.so",
@@ -33,8 +35,10 @@
           "includeFiles": [ "../Grpc.Core/Version.cs" ]
         },
         "copyToOutput": {
-          "include": "data/*",
           "mappings": {
+            "data/ca.pem": "../Grpc.IntegrationTesting/data/ca.pem",
+            "data/server1.key": "../Grpc.IntegrationTesting/data/server1.key",
+            "data/server1.pem": "../Grpc.IntegrationTesting/data/server1.pem",
             "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Release/grpc_csharp_ext.dll",
             "grpc_csharp_ext.x86.dll": "../../../vsprojects/Release/grpc_csharp_ext.dll",
             "libgrpc_csharp_ext.x64.so": "../../../libs/opt/libgrpc_csharp_ext.so",
@@ -44,11 +48,6 @@
       }
     }
   },
-  "runtimes": {
-    "win7-x64": { },
-    "debian.8-x64": { },
-    "osx.10.11-x64": { }
-  },
 
   "dependencies": {
     "Grpc.IntegrationTesting": {
@@ -57,13 +56,15 @@
   },
   "frameworks": {
     "net45": { },
-    "netstandard1.5": {
+    "netcoreapp1.0": {
       "imports": [
-        "portable-net45",
-        "net45"
+        "portable-net45"
       ],
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027"
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        }
       }
     }
   }
diff --git a/src/csharp/Grpc.IntegrationTesting.StressClient/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.StressClient/Properties/AssemblyInfo.cs
index e845bbf..9f9d728 100644
--- a/src/csharp/Grpc.IntegrationTesting.StressClient/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.IntegrationTesting.StressClient/Properties/AssemblyInfo.cs
@@ -1,4 +1,37 @@
-using System.Reflection;
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System.Reflection;
 using System.Runtime.CompilerServices;
 
 [assembly: AssemblyTitle("Grpc.IntegrationTesting.StressClient")]
diff --git a/src/csharp/Grpc.IntegrationTesting.StressClient/project.json b/src/csharp/Grpc.IntegrationTesting.StressClient/project.json
index 6b61a4b..1b900c8 100644
--- a/src/csharp/Grpc.IntegrationTesting.StressClient/project.json
+++ b/src/csharp/Grpc.IntegrationTesting.StressClient/project.json
@@ -13,8 +13,10 @@
           "includeFiles": [ "../Grpc.Core/Version.cs" ]
         },
         "copyToOutput": {
-          "include": "data/*",
           "mappings": {
+            "data/ca.pem": "../Grpc.IntegrationTesting/data/ca.pem",
+            "data/server1.key": "../Grpc.IntegrationTesting/data/server1.key",
+            "data/server1.pem": "../Grpc.IntegrationTesting/data/server1.pem",
             "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Debug/grpc_csharp_ext.dll",
             "grpc_csharp_ext.x86.dll": "../../../vsprojects/Debug/grpc_csharp_ext.dll",
             "libgrpc_csharp_ext.x64.so": "../../../libs/dbg/libgrpc_csharp_ext.so",
@@ -33,8 +35,10 @@
           "includeFiles": [ "../Grpc.Core/Version.cs" ]
         },
         "copyToOutput": {
-          "include": "data/*",
           "mappings": {
+            "data/ca.pem": "../Grpc.IntegrationTesting/data/ca.pem",
+            "data/server1.key": "../Grpc.IntegrationTesting/data/server1.key",
+            "data/server1.pem": "../Grpc.IntegrationTesting/data/server1.pem",
             "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Release/grpc_csharp_ext.dll",
             "grpc_csharp_ext.x86.dll": "../../../vsprojects/Release/grpc_csharp_ext.dll",
             "libgrpc_csharp_ext.x64.so": "../../../libs/opt/libgrpc_csharp_ext.so",
@@ -44,11 +48,6 @@
       }
     }
   },
-  "runtimes": {
-    "win7-x64": { },
-    "debian.8-x64": { },
-    "osx.10.11-x64": { }
-  },
 
   "dependencies": {
     "Grpc.IntegrationTesting": {
@@ -57,13 +56,15 @@
   },
   "frameworks": {
     "net45": { },
-    "netstandard1.5": {
+    "netcoreapp1.0": {
       "imports": [
-        "portable-net45",
-        "net45"
+        "portable-net45"
       ],
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027"
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        }
       }
     }
   }
diff --git a/src/csharp/Grpc.IntegrationTesting/GeneratedClientTest.cs b/src/csharp/Grpc.IntegrationTesting/GeneratedClientTest.cs
index eb7b55a..ac6c8d0 100644
--- a/src/csharp/Grpc.IntegrationTesting/GeneratedClientTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/GeneratedClientTest.cs
@@ -48,8 +48,6 @@
     {
         TestService.TestServiceClient unimplementedClient = new UnimplementedTestServiceClient();
 
-        // TODO: replace Moq by some mocking library with CoreCLR support.
-#if !NETSTANDARD1_5
         [Test]
         public void ExpandedParamOverloadCanBeMocked()
         {
@@ -72,7 +70,6 @@
 
             Assert.AreSame(expected, mockClient.Object.UnaryCall(new SimpleRequest(), new CallOptions()));
         }
-#endif
 
         [Test]
         public void DefaultMethodStubThrows_UnaryCall()
diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
index 3a07642..afd85fb 100644
--- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
+++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
@@ -38,12 +38,6 @@
     <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="CommandLine">
-      <HintPath>..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath>
-    </Reference>
-    <Reference Include="Moq">
-      <HintPath>..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Net" />
     <Reference Include="System.Net.Http" />
@@ -51,18 +45,6 @@
     <Reference Include="BouncyCastle.Crypto">
       <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
     </Reference>
-    <Reference Include="Google.Apis.Auth">
-      <HintPath>..\packages\Google.Apis.Auth.1.11.1\lib\net45\Google.Apis.Auth.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Auth.PlatformServices">
-      <HintPath>..\packages\Google.Apis.Auth.1.11.1\lib\net45\Google.Apis.Auth.PlatformServices.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Core">
-      <HintPath>..\packages\Google.Apis.Core.1.11.1\lib\net45\Google.Apis.Core.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Protobuf">
-      <HintPath>..\packages\Google.Protobuf.3.0.0-beta3\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll</HintPath>
-    </Reference>
     <Reference Include="Newtonsoft.Json">
       <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
     </Reference>
@@ -70,11 +52,44 @@
       <HintPath>..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll</HintPath>
     </Reference>
     <Reference Include="System.Interactive.Async">
-      <HintPath>..\packages\Ix-Async.1.2.5\lib\net45\System.Interactive.Async.dll</HintPath>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
     </Reference>
     <Reference Include="nunitlite">
       <HintPath>..\packages\NUnitLite.3.2.0\lib\net45\nunitlite.dll</HintPath>
     </Reference>
+    <Reference Include="CommandLineParser.Unofficial">
+      <HintPath>..\packages\CommandLineParser.Unofficial.2.0.275\lib\net45\CommandLineParser.Unofficial.dll</HintPath>
+    </Reference>
+    <Reference Include="log4net">
+      <HintPath>..\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Core">
+      <HintPath>..\packages\Google.Apis.Core.1.16.0\lib\net45\Google.Apis.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="Zlib.Portable">
+      <HintPath>..\packages\Zlib.Portable.Signed.1.11.0\lib\portable-net4+sl5+wp8+win8+wpa81+MonoTouch+MonoAndroid\Zlib.Portable.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis">
+      <HintPath>..\packages\Google.Apis.1.16.0\lib\net45\Google.Apis.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.PlatformServices">
+      <HintPath>..\packages\Google.Apis.1.16.0\lib\net45\Google.Apis.PlatformServices.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth">
+      <HintPath>..\packages\Google.Apis.Auth.1.16.0\lib\net45\Google.Apis.Auth.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth.PlatformServices">
+      <HintPath>..\packages\Google.Apis.Auth.1.16.0\lib\net45\Google.Apis.Auth.PlatformServices.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Protobuf">
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
+    </Reference>
+    <Reference Include="Castle.Core">
+      <HintPath>..\packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="Moq">
+      <HintPath>..\packages\Moq.4.6.38-alpha\lib\net45\Moq.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\Grpc.Core\Version.cs">
@@ -149,4 +164,4 @@
   <ItemGroup>
     <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
index e27fe5b..79fd18b 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
@@ -56,24 +56,24 @@
     {
         private class ClientOptions
         {
-            [Option("server_host", DefaultValue = "127.0.0.1")]
+            [Option("server_host", Default = "127.0.0.1")]
             public string ServerHost { get; set; }
 
-            [Option("server_host_override", DefaultValue = TestCredentials.DefaultHostOverride)]
+            [Option("server_host_override", Default = TestCredentials.DefaultHostOverride)]
             public string ServerHostOverride { get; set; }
 
             [Option("server_port", Required = true)]
             public int ServerPort { get; set; }
 
-            [Option("test_case", DefaultValue = "large_unary")]
+            [Option("test_case", Default = "large_unary")]
             public string TestCase { get; set; }
 
             // Deliberately using nullable bool type to allow --use_tls=true syntax (as opposed to --use_tls)
-            [Option("use_tls", DefaultValue = false)]
+            [Option("use_tls", Default = false)]
             public bool? UseTls { get; set; }
 
             // Deliberately using nullable bool type to allow --use_test_ca=true syntax (as opposed to --use_test_ca)
-            [Option("use_test_ca", DefaultValue = false)]
+            [Option("use_test_ca", Default = false)]
             public bool? UseTestCa { get; set; }
 
             [Option("default_service_account", Required = false)]
@@ -84,19 +84,6 @@
 
             [Option("service_account_key_file", Required = false)]
             public string ServiceAccountKeyFile { get; set; }
-
-            [HelpOption]
-            public string GetUsage()
-            {
-                var help = new HelpText
-                {
-                    Heading = "gRPC C# interop testing client",
-                    AddDashesToOption = true
-                };
-                help.AddPreOptionsLine("Usage:");
-                help.AddOptions(this);
-                return help;
-            }
         }
 
         ClientOptions options;
@@ -108,14 +95,13 @@
 
         public static void Run(string[] args)
         {
-            var options = new ClientOptions();
-            if (!Parser.Default.ParseArguments(args, options))
-            {
-                Environment.Exit(1);
-            }
-
-            var interopClient = new InteropClient(options);
-            interopClient.Run().Wait();
+            var parserResult = Parser.Default.ParseArguments<ClientOptions>(args)
+                .WithNotParsed(errors => Environment.Exit(1))
+                .WithParsed(options =>
+                {
+                    var interopClient = new InteropClient(options);
+                    interopClient.Run().Wait();
+                });
         }
 
         private async Task Run()
@@ -145,26 +131,16 @@
 
             if (options.TestCase == "jwt_token_creds")
             {
-#if !NETSTANDARD1_5
                 var googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
                 Assert.IsTrue(googleCredential.IsCreateScopedRequired);
                 credentials = ChannelCredentials.Create(credentials, googleCredential.ToCallCredentials());
-#else
-                // TODO(jtattermusch): implement this
-                throw new NotImplementedException("Not supported on CoreCLR yet");
-#endif
             }
 
             if (options.TestCase == "compute_engine_creds")
             {
-#if !NETSTANDARD1_5
                 var googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
                 Assert.IsFalse(googleCredential.IsCreateScopedRequired);
                 credentials = ChannelCredentials.Create(credentials, googleCredential.ToCallCredentials());
-#else
-                // TODO(jtattermusch): implement this
-                throw new NotImplementedException("Not supported on CoreCLR yet");
-#endif
             }
             return credentials;
         }
@@ -395,7 +371,6 @@
 
         public static async Task RunOAuth2AuthTokenAsync(TestService.TestServiceClient client, string oauthScope)
         {
-#if !NETSTANDARD1_5
             Console.WriteLine("running oauth2_auth_token");
             ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope });
             string oauth2Token = await credential.GetAccessTokenForRequestAsync();
@@ -413,15 +388,10 @@
             Assert.True(oauthScope.Contains(response.OauthScope));
             Assert.AreEqual(GetEmailFromServiceAccountFile(), response.Username);
             Console.WriteLine("Passed!");
-#else
-            // TODO(jtattermusch): implement this
-            throw new NotImplementedException("Not supported on CoreCLR yet");
-#endif
         }
 
         public static async Task RunPerRpcCredsAsync(TestService.TestServiceClient client, string oauthScope)
         {
-#if !NETSTANDARD1_5
             Console.WriteLine("running per_rpc_creds");
             ITokenAccess googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
 
@@ -435,10 +405,6 @@
 
             Assert.AreEqual(GetEmailFromServiceAccountFile(), response.Username);
             Console.WriteLine("Passed!");
-#else
-            // TODO(jtattermusch): implement this
-            throw new NotImplementedException("Not supported on CoreCLR yet");
-#endif
         }
 
         public static async Task RunCancelAfterBeginAsync(TestService.TestServiceClient client)
@@ -731,17 +697,12 @@
         // extracts the client_email field from service account file used for auth test cases
         private static string GetEmailFromServiceAccountFile()
         {
-#if !NETSTANDARD1_5
             string keyFile = Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS");
             Assert.IsNotNull(keyFile);
             var jobject = JObject.Parse(File.ReadAllText(keyFile));
             string email = jobject.GetValue("client_email").Value<string>();
             Assert.IsTrue(email.Length > 0);  // spec requires nonempty client email.
             return email;
-#else
-            // TODO(jtattermusch): implement this
-            throw new NotImplementedException("Not supported on CoreCLR yet");
-#endif
         }
 
         private static Metadata CreateTestMetadata()
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
index cd47e31..4118f99 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
@@ -51,25 +51,12 @@
     {
         private class ServerOptions
         {
-            [Option("port", DefaultValue = 8070)]
+            [Option("port", Default = 8070)]
             public int Port { get; set; }
 
             // Deliberately using nullable bool type to allow --use_tls=true syntax (as opposed to --use_tls)
-            [Option("use_tls", DefaultValue = false)]
+            [Option("use_tls", Default = false)]
             public bool? UseTls { get; set; }
-
-            [HelpOption]
-            public string GetUsage()
-            {
-                var help = new HelpText
-                {
-                    Heading = "gRPC C# interop testing server",
-                    AddDashesToOption = true
-                };
-                help.AddPreOptionsLine("Usage:");
-                help.AddOptions(this);
-                return help;
-            }
         }
 
         ServerOptions options;
@@ -81,14 +68,13 @@
 
         public static void Run(string[] args)
         {
-            var options = new ServerOptions();
-            if (!Parser.Default.ParseArguments(args, options))
-            {
-                Environment.Exit(1);
-            }
-
-            var interopServer = new InteropServer(options);
-            interopServer.Run();
+            var parserResult = Parser.Default.ParseArguments<ServerOptions>(args)
+                .WithNotParsed(errors => Environment.Exit(1))
+                .WithParsed(options =>
+                {
+                    var interopServer = new InteropServer(options);
+                    interopServer.Run();
+                });
         }
 
         private void Run()
diff --git a/src/csharp/Grpc.IntegrationTesting/NUnitMain.cs b/src/csharp/Grpc.IntegrationTesting/NUnitMain.cs
index 100ff0b..21c8adb 100644
--- a/src/csharp/Grpc.IntegrationTesting/NUnitMain.cs
+++ b/src/csharp/Grpc.IntegrationTesting/NUnitMain.cs
@@ -49,7 +49,7 @@
         {
             // Make logger immune to NUnit capturing stdout and stderr to workaround https://github.com/nunit/nunit/issues/1406.
             GrpcEnvironment.SetLogger(new TextWriterLogger(Console.Error));
-#if NETSTANDARD1_5
+#if NETCOREAPP1_0
             return new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args, new ExtendedTextWrapper(Console.Out), Console.In);
 #else
             return new AutoRun().Execute(args);
diff --git a/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs
index 1beb0bb..2a455e3 100644
--- a/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs
+++ b/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs
@@ -1,3 +1,36 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
diff --git a/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs b/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs
index a7c9fa8..865556c 100644
--- a/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs
+++ b/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs
@@ -52,21 +52,8 @@
     {
         private class ServerOptions
         {
-            [Option("driver_port", DefaultValue = 0)]
+            [Option("driver_port", Default = 0)]
             public int DriverPort { get; set; }
-
-            [HelpOption]
-            public string GetUsage()
-            {
-                var help = new HelpText
-                {
-                    Heading = "gRPC C# performance testing worker",
-                    AddDashesToOption = true
-                };
-                help.AddPreOptionsLine("Usage:");
-                help.AddOptions(this);
-                return help;
-            }
         }
 
         ServerOptions options;
@@ -78,14 +65,13 @@
 
         public static void Run(string[] args)
         {
-            var options = new ServerOptions();
-            if (!Parser.Default.ParseArguments(args, options))
-            {
-                Environment.Exit(1);
-            }
-
-            var workerServer = new QpsWorker(options);
-            workerServer.RunAsync().Wait();
+            var parserResult = Parser.Default.ParseArguments<ServerOptions>(args)
+                .WithNotParsed((x) => Environment.Exit(1))
+                .WithParsed(options =>
+                {
+                    var workerServer = new QpsWorker(options);
+                    workerServer.RunAsync().Wait();
+                });
         }
 
         private async Task RunAsync()
diff --git a/src/csharp/Grpc.IntegrationTesting/StressTestClient.cs b/src/csharp/Grpc.IntegrationTesting/StressTestClient.cs
index 74ee040..750613b 100644
--- a/src/csharp/Grpc.IntegrationTesting/StressTestClient.cs
+++ b/src/csharp/Grpc.IntegrationTesting/StressTestClient.cs
@@ -54,36 +54,23 @@
 
         private class ClientOptions
         {
-            [Option("server_addresses", DefaultValue = "localhost:8080")]
+            [Option("server_addresses", Default = "localhost:8080")]
             public string ServerAddresses { get; set; }
 
-            [Option("test_cases", DefaultValue = "large_unary:100")]
+            [Option("test_cases", Default = "large_unary:100")]
             public string TestCases { get; set; }
 
-            [Option("test_duration_secs", DefaultValue = -1)]
+            [Option("test_duration_secs", Default = -1)]
             public int TestDurationSecs { get; set; }
 
-            [Option("num_channels_per_server", DefaultValue = 1)]
+            [Option("num_channels_per_server", Default = 1)]
             public int NumChannelsPerServer { get; set; }
 
-            [Option("num_stubs_per_channel", DefaultValue = 1)]
+            [Option("num_stubs_per_channel", Default = 1)]
             public int NumStubsPerChannel { get; set; }
 
-            [Option("metrics_port", DefaultValue = 8081)]
+            [Option("metrics_port", Default = 8081)]
             public int MetricsPort { get; set; }
-
-            [HelpOption]
-            public string GetUsage()
-            {
-                var help = new HelpText
-                {
-                    Heading = "gRPC C# stress test client",
-                    AddDashesToOption = true
-                };
-                help.AddPreOptionsLine("Usage:");
-                help.AddOptions(this);
-                return help;
-            }
         }
 
         ClientOptions options;
@@ -105,23 +92,21 @@
 
         public static void Run(string[] args)
         {
-            var options = new ClientOptions();
-            if (!Parser.Default.ParseArguments(args, options))
-            {
-                Environment.Exit(1);
-            }
+            var parserResult = Parser.Default.ParseArguments<ClientOptions>(args)
+                .WithNotParsed((x) => Environment.Exit(1))
+                .WithParsed(options => {
+                    GrpcPreconditions.CheckArgument(options.NumChannelsPerServer > 0);
+                    GrpcPreconditions.CheckArgument(options.NumStubsPerChannel > 0);
 
-            GrpcPreconditions.CheckArgument(options.NumChannelsPerServer > 0);
-            GrpcPreconditions.CheckArgument(options.NumStubsPerChannel > 0);
+                    var serverAddresses = options.ServerAddresses.Split(',');
+                    GrpcPreconditions.CheckArgument(serverAddresses.Length > 0, "You need to provide at least one server address");
 
-            var serverAddresses = options.ServerAddresses.Split(',');
-            GrpcPreconditions.CheckArgument(serverAddresses.Length > 0, "You need to provide at least one server address");
+                    var testCases = ParseWeightedTestCases(options.TestCases);
+                    GrpcPreconditions.CheckArgument(testCases.Count > 0, "You need to provide at least one test case");
 
-            var testCases = ParseWeightedTestCases(options.TestCases);
-            GrpcPreconditions.CheckArgument(testCases.Count > 0, "You need to provide at least one test case");
-
-            var interopClient = new StressTestClient(options, serverAddresses.ToList(), testCases);
-            interopClient.Run().Wait();
+                    var interopClient = new StressTestClient(options, serverAddresses.ToList(), testCases);
+                    interopClient.Run().Wait();
+                });
         }
 
         async Task Run()
diff --git a/src/csharp/Grpc.IntegrationTesting/packages.config b/src/csharp/Grpc.IntegrationTesting/packages.config
index 3161c5b..a39fb3a 100644
--- a/src/csharp/Grpc.IntegrationTesting/packages.config
+++ b/src/csharp/Grpc.IntegrationTesting/packages.config
@@ -1,13 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
-  <package id="CommandLineParser" version="1.9.71" targetFramework="net45" />
-  <package id="Google.Apis.Auth" version="1.11.1" targetFramework="net45" />
-  <package id="Google.Apis.Core" version="1.11.1" targetFramework="net45" />
-  <package id="Google.Protobuf" version="3.0.0-beta3" targetFramework="net45" />
-  <package id="Ix-Async" version="1.2.5" targetFramework="net45" />
-  <package id="Moq" version="4.2.1510.2205" targetFramework="net45" />
+  <package id="Castle.Core" version="3.3.3" targetFramework="net45" />
+  <package id="CommandLineParser.Unofficial" version="2.0.275" targetFramework="net45" />
+  <package id="Google.Apis" version="1.16.0" targetFramework="net45" />
+  <package id="Google.Apis.Auth" version="1.16.0" targetFramework="net45" />
+  <package id="Google.Apis.Core" version="1.16.0" targetFramework="net45" />
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
+  <package id="log4net" version="2.0.3" targetFramework="net45" />
+  <package id="Moq" version="4.6.38-alpha" targetFramework="net45" />
   <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
   <package id="NUnit" version="3.2.0" targetFramework="net45" />
   <package id="NUnitLite" version="3.2.0" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
+  <package id="Zlib.Portable.Signed" version="1.11.0" targetFramework="net45" />
 </packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.IntegrationTesting/project.json b/src/csharp/Grpc.IntegrationTesting/project.json
index dcd9cca..0225abb 100644
--- a/src/csharp/Grpc.IntegrationTesting/project.json
+++ b/src/csharp/Grpc.IntegrationTesting/project.json
@@ -13,8 +13,10 @@
           "includeFiles": [ "../Grpc.Core/Version.cs" ]
         },
         "copyToOutput": {
-          "include": "data/*",
           "mappings": {
+            "data/ca.pem": "../Grpc.IntegrationTesting/data/ca.pem",
+            "data/server1.key": "../Grpc.IntegrationTesting/data/server1.key",
+            "data/server1.pem": "../Grpc.IntegrationTesting/data/server1.pem",
             "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Debug/grpc_csharp_ext.dll",
             "grpc_csharp_ext.x86.dll": "../../../vsprojects/Debug/grpc_csharp_ext.dll",
             "libgrpc_csharp_ext.x64.so": "../../../libs/dbg/libgrpc_csharp_ext.so",
@@ -33,8 +35,10 @@
           "includeFiles": [ "../Grpc.Core/Version.cs" ]
         },
         "copyToOutput": {
-          "include": "data/*",
           "mappings": {
+            "data/ca.pem": "../Grpc.IntegrationTesting/data/ca.pem",
+            "data/server1.key": "../Grpc.IntegrationTesting/data/server1.key",
+            "data/server1.pem": "../Grpc.IntegrationTesting/data/server1.pem",
             "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Release/grpc_csharp_ext.dll",
             "grpc_csharp_ext.x86.dll": "../../../vsprojects/Release/grpc_csharp_ext.dll",
             "libgrpc_csharp_ext.x64.so": "../../../libs/opt/libgrpc_csharp_ext.so",
@@ -44,11 +48,6 @@
       }
     }
   },
-  "runtimes": {
-    "win7-x64": { },
-    "debian.8-x64": { },
-    "osx.10.11-x64": { }
-  },
 
   "dependencies": {
     "Grpc.Auth": {
@@ -57,29 +56,29 @@
     "Grpc.Core": {
       "target": "project"
     },
-    "Google.Protobuf": "3.0.0-beta3",
-    "CommandLineParser": "1.9.71",
+    "Google.Protobuf": "3.0.0",
+    "CommandLineParser.Unofficial": "2.0.275",
+    "Moq": "4.6.38-alpha",
     "NUnit": "3.2.0",
     "NUnitLite": "3.2.0-*"
   },
   "frameworks": {
     "net45": {
-      "dependencies": {
-        "Moq": "4.2.1510.2205"
-      },
       "frameworkAssemblies": {
         "System.Runtime": "",
         "System.IO": ""
       }
     },
-    "netstandard1.5": {
+    "netcoreapp1.0": {
       "imports": [
-        "portable-net45",
-        "net45"
+        "portable-net45"
       ],
       "dependencies": {
-        "NETStandard.Library": "1.5.0-rc2-24027",
-        "System.Linq.Expressions": "4.0.11-rc2-24027"
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        },
+        "System.Linq.Expressions": "4.1.0"
       }
     }
   }
diff --git a/src/csharp/Grpc.Tools.nuspec b/src/csharp/Grpc.Tools.nuspec
index 31d1bed..0c937ab 100644
--- a/src/csharp/Grpc.Tools.nuspec
+++ b/src/csharp/Grpc.Tools.nuspec
@@ -16,17 +16,18 @@
     <tags>gRPC RPC Protocol HTTP/2</tags>
   </metadata>
   <files>
-    <file src="protoc_plugins\windows_x86\protoc.exe" target="tools\windows_x86\protoc.exe" />
-    <file src="protoc_plugins\windows_x86\grpc_csharp_plugin.exe" target="tools\windows_x86\grpc_csharp_plugin.exe" />
-    <file src="protoc_plugins\windows_x64\protoc.exe" target="tools\windows_x64\protoc.exe" />
-    <file src="protoc_plugins\windows_x64\grpc_csharp_plugin.exe" target="tools\windows_x64\grpc_csharp_plugin.exe" />
-    <file src="protoc_plugins\linux_x86\protoc" target="tools\linux_x86\protoc" />
-    <file src="protoc_plugins\linux_x86\grpc_csharp_plugin" target="tools\linux_x86\grpc_csharp_plugin" />
-    <file src="protoc_plugins\linux_x64\protoc" target="tools\linux_x64\protoc" />
-    <file src="protoc_plugins\linux_x64\grpc_csharp_plugin" target="tools\linux_x64\grpc_csharp_plugin" />
-    <file src="protoc_plugins\macosx_x86\protoc" target="tools\macosx_x86\protoc" />
-    <file src="protoc_plugins\macosx_x86\grpc_csharp_plugin" target="tools\macosx_x86\grpc_csharp_plugin" />
-    <file src="protoc_plugins\macosx_x64\protoc" target="tools\macosx_x64\protoc" />
-    <file src="protoc_plugins\macosx_x64\grpc_csharp_plugin" target="tools\macosx_x64\grpc_csharp_plugin" />
+    <!-- forward slashes in src path enable building on Linux -->
+    <file src="protoc_plugins/windows_x86/protoc.exe" target="tools\windows_x86\protoc.exe" />
+    <file src="protoc_plugins/windows_x86/grpc_csharp_plugin.exe" target="tools\windows_x86\grpc_csharp_plugin.exe" />
+    <file src="protoc_plugins/windows_x64/protoc.exe" target="tools\windows_x64\protoc.exe" />
+    <file src="protoc_plugins/windows_x64/grpc_csharp_plugin.exe" target="tools\windows_x64\grpc_csharp_plugin.exe" />
+    <file src="protoc_plugins/linux_x86/protoc" target="tools\linux_x86\protoc" />
+    <file src="protoc_plugins/linux_x86/grpc_csharp_plugin" target="tools\linux_x86\grpc_csharp_plugin" />
+    <file src="protoc_plugins/linux_x64/protoc" target="tools\linux_x64\protoc" />
+    <file src="protoc_plugins/linux_x64/grpc_csharp_plugin" target="tools\linux_x64\grpc_csharp_plugin" />
+    <file src="protoc_plugins/macosx_x86/protoc" target="tools\macosx_x86\protoc" />
+    <file src="protoc_plugins/macosx_x86/grpc_csharp_plugin" target="tools\macosx_x86\grpc_csharp_plugin" />
+    <file src="protoc_plugins/macosx_x64/protoc" target="tools\macosx_x64\protoc" />
+    <file src="protoc_plugins/macosx_x64/grpc_csharp_plugin" target="tools\macosx_x64\grpc_csharp_plugin" />
   </files>
 </package>
diff --git a/src/csharp/build_packages.bat b/src/csharp/build_packages.bat
index f05c024..c0036df 100644
--- a/src/csharp/build_packages.bat
+++ b/src/csharp/build_packages.bat
@@ -29,12 +29,13 @@
 
 @rem Builds gRPC NuGet packages
 
+@rem This way of building nuget packages is now obsolete. C# nuget packages
+@rem with CoreCLR support are now being built using the dotnet cli
+@rem in build_packages_dotnetcli.sh
+
 @rem Current package versions
 set VERSION=1.1.0-dev
-set PROTOBUF_VERSION=3.0.0-beta3
-
-@rem Packages that depend on prerelease packages (like Google.Protobuf) need to have prerelease suffix as well.
-set VERSION_WITH_BETA=%VERSION%-beta
+set PROTOBUF_VERSION=3.0.0
 
 @rem Adjust the location of nuget.exe
 set NUGET=C:\nuget\nuget.exe
@@ -58,7 +59,6 @@
 
 @rem Fetch all dependencies
 %NUGET% restore ..\..\vsprojects\grpc_csharp_ext.sln || goto :error
-%NUGET% restore Grpc.sln || goto :error
 
 setlocal
 
@@ -73,7 +73,7 @@
 
 %NUGET% pack Grpc.Auth\Grpc.Auth.nuspec -Symbols -Version %VERSION% || goto :error
 %NUGET% pack Grpc.Core\Grpc.Core.nuspec -Symbols -Version %VERSION% || goto :error
-%NUGET% pack Grpc.HealthCheck\Grpc.HealthCheck.nuspec -Symbols -Version %VERSION_WITH_BETA% -Properties ProtobufVersion=%PROTOBUF_VERSION% || goto :error
+%NUGET% pack Grpc.HealthCheck\Grpc.HealthCheck.nuspec -Symbols -Version %VERSION% -Properties ProtobufVersion=%PROTOBUF_VERSION% || goto :error
 %NUGET% pack Grpc.nuspec -Version %VERSION% || goto :error
 %NUGET% pack Grpc.Tools.nuspec -Version %VERSION% || goto :error
 
@@ -81,8 +81,8 @@
 xcopy /Y /I *.nupkg ..\..\artifacts\
 
 @rem create a zipfile with the artifacts as well
-powershell -Command "Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('..\..\artifacts', 'csharp_nugets.zip');"
-xcopy /Y /I csharp_nugets.zip ..\..\artifacts\
+powershell -Command "Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('..\..\artifacts', 'csharp_nugets_obsolete.zip');"
+xcopy /Y /I csharp_nugets_obsolete.zip ..\..\artifacts\
 
 goto :EOF
 
diff --git a/src/csharp/build_packages_dotnetcli.sh b/src/csharp/build_packages_dotnetcli.sh
new file mode 100755
index 0000000..3c127f3
--- /dev/null
+++ b/src/csharp/build_packages_dotnetcli.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -ex
+
+cd $(dirname $0)
+
+mkdir -p ../../artifacts/
+
+mkdir -p nativelibs/windows_x86 nativelibs/windows_x64 \
+    nativelibs/linux_x86 nativelibs/linux_x64 \
+    nativelibs/macosx_x86 nativelibs/macosx_x64
+
+mkdir -p protoc_plugins/windows_x86 protoc_plugins/windows_x64 \
+    protoc_plugins/linux_x86 protoc_plugins/linux_x64 \
+    protoc_plugins/macosx_x86 protoc_plugins/macosx_x64
+
+# Collect the artifacts built by the previous build step if running on Jenkins
+cp $EXTERNAL_GIT_ROOT/architecture=x86,language=csharp,platform=windows/artifacts/* nativelibs/windows_x86 || true
+cp $EXTERNAL_GIT_ROOT/architecture=x64,language=csharp,platform=windows/artifacts/* nativelibs/windows_x64 || true
+cp $EXTERNAL_GIT_ROOT/architecture=x86,language=csharp,platform=linux/artifacts/* nativelibs/linux_x86 || true
+cp $EXTERNAL_GIT_ROOT/architecture=x64,language=csharp,platform=linux/artifacts/* nativelibs/linux_x64 || true
+cp $EXTERNAL_GIT_ROOT/architecture=x86,language=csharp,platform=macos/artifacts/* nativelibs/macosx_x86 || true
+cp $EXTERNAL_GIT_ROOT/architecture=x64,language=csharp,platform=macos/artifacts/* nativelibs/macosx_x64 || true
+
+# Collect protoc artifacts built by the previous build step
+cp $EXTERNAL_GIT_ROOT/architecture=x86,language=protoc,platform=windows/artifacts/* protoc_plugins/windows_x86 || true
+cp $EXTERNAL_GIT_ROOT/architecture=x64,language=protoc,platform=windows/artifacts/* protoc_plugins/windows_x64 || true
+cp $EXTERNAL_GIT_ROOT/architecture=x86,language=protoc,platform=linux/artifacts/* protoc_plugins/linux_x86 || true
+cp $EXTERNAL_GIT_ROOT/architecture=x64,language=protoc,platform=linux/artifacts/* protoc_plugins/linux_x64 || true
+cp $EXTERNAL_GIT_ROOT/architecture=x86,language=protoc,platform=macos/artifacts/* protoc_plugins/macosx_x86 || true
+cp $EXTERNAL_GIT_ROOT/architecture=x64,language=protoc,platform=macos/artifacts/* protoc_plugins/macosx_x64 || true
+
+dotnet restore .
+
+dotnet pack --configuration Release Grpc.Core/project.json --output ../../artifacts
+dotnet pack --configuration Release Grpc.Auth/project.json --output ../../artifacts
+dotnet pack --configuration Release Grpc.HealthCheck/project.json --output ../../artifacts
+
+nuget pack Grpc.nuspec -Version "1.1.0-dev" -OutputDirectory ../../artifacts
+nuget pack Grpc.Tools.nuspec -Version "1.1.0-dev" -OutputDirectory ../../artifacts
+
+(cd ../../artifacts && zip csharp_nugets_dotnetcli.zip *.nupkg)
diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
index 3d0947c..068bf70 100644
--- a/src/csharp/ext/grpc_csharp_ext.c
+++ b/src/csharp/ext/grpc_csharp_ext.c
@@ -255,8 +255,10 @@
   }
 
   GPR_ASSERT(grpc_byte_buffer_reader_init(&reader, ctx->recv_message));
+  intptr_t result = (intptr_t)grpc_byte_buffer_length(reader.buffer_out);
+  grpc_byte_buffer_reader_destroy(&reader);
 
-  return (intptr_t)grpc_byte_buffer_length(reader.buffer_out);
+  return result;
 }
 
 /*
@@ -279,6 +281,8 @@
     offset += len;
     gpr_slice_unref(slice);
   }
+
+  grpc_byte_buffer_reader_destroy(&reader);
 }
 
 GPR_EXPORT grpc_status_code GPR_CALLTYPE
diff --git a/src/node/README.md b/src/node/README.md
index 15d4c6d..9eafce9 100644
--- a/src/node/README.md
+++ b/src/node/README.md
@@ -39,7 +39,7 @@
 To run the test suite, simply run `npm test` in the install location.
 
 ## API
-This library internally uses [ProtoBuf.js](https://github.com/dcodeIO/ProtoBuf.js), and some structures it exports match those exported by that library
+This library internally uses [ProtoBuf.js](https://github.com/dcodeIO/ProtoBuf.js), and some structures it exports match those exported by that library.
 
 If you require this module, you will get an object with the following members
 
@@ -63,7 +63,7 @@
 Returns the same structure that `load` returns, but takes a reflection object from `ProtoBuf.js` instead of a file name.
 
 ```javascript
-function Server([serverOpions])
+function Server([serverOptions])
 ```
 
 Constructs a server to which service/implementation pairs can be added.
diff --git a/src/node/ext/server_credentials.cc b/src/node/ext/server_credentials.cc
index a817ade..0ff58bb 100644
--- a/src/node/ext/server_credentials.cc
+++ b/src/node/ext/server_credentials.cc
@@ -169,18 +169,18 @@
   for(uint32_t i = 0; i < key_cert_pair_count; i++) {
     Local<Value> pair_val = Nan::Get(pair_list, i).ToLocalChecked();
     if (!pair_val->IsObject()) {
-      delete key_cert_pairs;
+      delete[] key_cert_pairs;
       return Nan::ThrowTypeError("Key/cert pairs must be objects");
     }
     Local<Object> pair_obj = Nan::To<Object>(pair_val).ToLocalChecked();
     Local<Value> maybe_key = Nan::Get(pair_obj, key_key).ToLocalChecked();
     Local<Value> maybe_cert = Nan::Get(pair_obj, cert_key).ToLocalChecked();
     if (!::node::Buffer::HasInstance(maybe_key)) {
-      delete key_cert_pairs;
+      delete[] key_cert_pairs;
       return Nan::ThrowTypeError("private_key must be a Buffer");
     }
     if (!::node::Buffer::HasInstance(maybe_cert)) {
-      delete key_cert_pairs;
+      delete[] key_cert_pairs;
       return Nan::ThrowTypeError("cert_chain must be a Buffer");
     }
     key_cert_pairs[i].private_key = ::node::Buffer::Data(maybe_key);
@@ -189,7 +189,7 @@
   grpc_server_credentials *creds = grpc_ssl_server_credentials_create_ex(
       root_certs, key_cert_pairs, key_cert_pair_count,
       client_certificate_request, NULL);
-  delete key_cert_pairs;
+  delete[] key_cert_pairs;
   if (creds == NULL) {
     info.GetReturnValue().SetNull();
   } else {
diff --git a/src/node/health_check/package.json b/src/node/health_check/package.json
index 40e2760..e673359 100644
--- a/src/node/health_check/package.json
+++ b/src/node/health_check/package.json
@@ -15,9 +15,9 @@
     }
   ],
   "dependencies": {
-    "grpc": "^0.15.0",
+    "grpc": "^1.1.0-dev",
     "lodash": "^3.9.3",
-    "google-protobuf": "^3.0.0-alpha.5"
+    "google-protobuf": "^3.0.0"
   },
   "files": [
     "LICENSE",
diff --git a/src/node/src/credentials.js b/src/node/src/credentials.js
index 043df06..51ff1da 100644
--- a/src/node/src/credentials.js
+++ b/src/node/src/credentials.js
@@ -71,6 +71,8 @@
 
 var common = require('./common.js');
 
+var _ = require('lodash');
+
 /**
  * Create an SSL Credentials object. If using a client-side certificate, both
  * the second and third arguments must be passed.
@@ -99,7 +101,7 @@
       var message = '';
       if (error) {
         message = error.message;
-        if (error.hasOwnProperty('code')) {
+        if (error.hasOwnProperty('code') && _.isFinite(error.code)) {
           code = error.code;
         } else {
           code = grpc.status.UNAUTHENTICATED;
diff --git a/src/node/test/credentials_test.js b/src/node/test/credentials_test.js
index 0a21572..305843f 100644
--- a/src/node/test/credentials_test.js
+++ b/src/node/test/credentials_test.js
@@ -71,7 +71,10 @@
 var fakeFailingGoogleCredentials = {
   getRequestMetadata: function(service_url, callback) {
     setTimeout(function() {
-      callback(new Error('Authentication failure'));
+      // Google credentials currently adds string error codes to auth errors
+      var error = new Error('Authentication failure');
+      error.code = 'ENOENT';
+      callback(error);
     }, 0);
   }
 };
diff --git a/src/node/tools/bin/protoc.js b/src/node/tools/bin/protoc.js
index 53fc5dc..7f83568 100755
--- a/src/node/tools/bin/protoc.js
+++ b/src/node/tools/bin/protoc.js
@@ -47,7 +47,11 @@
 
 var protoc = path.resolve(__dirname, 'protoc' + exe_ext);
 
-var child_process = execFile(protoc, process.argv.slice(2), function(error, stdout, stderr) {
+var plugin = path.resolve(__dirname, 'grpc_node_plugin' + exe_ext);
+
+var args = ['--plugin=protoc-gen-grpc=' + plugin].concat(process.argv.slice(2));
+
+var child_process = execFile(protoc, args, function(error, stdout, stderr) {
   if (error) {
     throw error;
   }
diff --git "a/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec" "b/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
index 97f4f58..0c3c321 100644
--- "a/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
+++ "b/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
@@ -36,7 +36,7 @@
   # exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
   # before them.
   s.name     = '!ProtoCompiler-gRPCPlugin'
-  v = '1.0.0-pre1'
+  v = '1.0.0'
   s.version  = v
   s.summary  = 'The gRPC ProtoC plugin generates Objective-C files from .proto services.'
   s.description = <<-DESC
@@ -82,10 +82,9 @@
   s.authors  = { 'The gRPC contributors' => 'grpc-packages@google.com' }
 
   repo = 'grpc/grpc'
-  release = "objective-c-v#{v}"
   file = "grpc_objective_c_plugin-#{v}-macos-x86_64.zip"
   s.source = {
-    :http => "https://github.com/#{repo}/releases/download/#{release}/#{file}",
+    :http => "https://github.com/#{repo}/releases/download/v#{v}/#{file}",
     # TODO(jcanizales): Add sha1 or sha256
     # :sha1 => '??',
   }
@@ -96,7 +95,7 @@
   s.preserve_paths = plugin
 
   # Restrict the protoc version to the one supported by this plugin.
-  s.dependency '!ProtoCompiler', '3.0.0-beta-3.1'
+  s.dependency '!ProtoCompiler', '3.0.0'
   # For the Protobuf dependency not to complain:
   s.ios.deployment_target = '7.1'
   s.osx.deployment_target = '10.9'
diff --git "a/src/objective-c/\041ProtoCompiler.podspec" "b/src/objective-c/\041ProtoCompiler.podspec"
index 56aacc3..5018ded 100644
--- "a/src/objective-c/\041ProtoCompiler.podspec"
+++ "b/src/objective-c/\041ProtoCompiler.podspec"
@@ -36,7 +36,7 @@
   # exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
   # before them.
   s.name     = '!ProtoCompiler'
-  v = '3.0.0-beta-3.1'
+  v = '3.0.0'
   s.version  = v
   s.summary  = 'The Protobuf Compiler (protoc) generates Objective-C files from .proto files'
   s.description = <<-DESC
@@ -108,7 +108,7 @@
                      'google/**/*.proto' # Well-known protobuf types
 
   # Restrict the protobuf runtime version to the one supported by this version of protoc.
-  s.dependency 'Protobuf', v
+  s.dependency 'Protobuf', '~> 3.0'
   # For the Protobuf dependency not to complain:
   s.ios.deployment_target = '7.1'
   s.osx.deployment_target = '10.9'
@@ -120,7 +120,7 @@
   repo_root = '../..'
   plugin = 'grpc_objective_c_plugin'
   s.prepare_command = <<-CMD
-    if [ ! -f protoc ]; then
+    if [ ! -f bin/protoc ]; then
       cd #{repo_root}
       # This will build protoc from the Protobuf submodule of gRPC, and put it in
       # #{repo_root}/bins/opt/protobuf.
@@ -129,7 +129,9 @@
       # _we do not want_. Find a way for this to always build from source.
       make #{plugin}
       cd -
+    else
+      mv bin/protoc .
+      mv include/google .
     fi
   CMD
-
 end
diff --git a/src/objective-c/BoringSSL.podspec b/src/objective-c/BoringSSL.podspec
index b759997..e14f39b 100644
--- a/src/objective-c/BoringSSL.podspec
+++ b/src/objective-c/BoringSSL.podspec
@@ -31,7 +31,7 @@
 
 Pod::Spec.new do |s|
   s.name     = 'BoringSSL'
-  version = '5.0'
+  version = '6.0'
   s.version  = version
   s.summary  = 'BoringSSL is a fork of OpenSSL that is designed to meet Google’s needs.'
   # Adapted from the homepage:
@@ -70,7 +70,7 @@
   s.source = {
     :git => 'https://boringssl.googlesource.com/boringssl',
     :tag => "version_for_cocoapods_#{version}",
-    # :commit => '8d343b44bbab829d1a28fdef650ca95f7db4412e',
+    # :commit => '4ac2dc4c0d48ca45da4f66c40e60d6b425fa94a3',
   }
 
   name = 'openssl'
@@ -388,42 +388,42 @@
           0x28340c19,
           0x283480ac,
           0x283500ea,
-          0x2c3227cb,
-          0x2c32a7d9,
-          0x2c3327eb,
-          0x2c33a7fd,
-          0x2c342811,
-          0x2c34a823,
-          0x2c35283e,
-          0x2c35a850,
-          0x2c362863,
+          0x2c322843,
+          0x2c32a851,
+          0x2c332863,
+          0x2c33a875,
+          0x2c342889,
+          0x2c34a89b,
+          0x2c3528b6,
+          0x2c35a8c8,
+          0x2c3628db,
           0x2c36832d,
-          0x2c372870,
-          0x2c37a882,
-          0x2c382895,
-          0x2c38a8ac,
-          0x2c3928ba,
-          0x2c39a8ca,
-          0x2c3a28dc,
-          0x2c3aa8f0,
-          0x2c3b2901,
-          0x2c3ba920,
-          0x2c3c2934,
-          0x2c3ca94a,
-          0x2c3d2963,
-          0x2c3da980,
-          0x2c3e2991,
-          0x2c3ea99f,
-          0x2c3f29b7,
-          0x2c3fa9cf,
-          0x2c4029dc,
+          0x2c3728e8,
+          0x2c37a8fa,
+          0x2c38290d,
+          0x2c38a924,
+          0x2c392932,
+          0x2c39a942,
+          0x2c3a2954,
+          0x2c3aa968,
+          0x2c3b2979,
+          0x2c3ba998,
+          0x2c3c29ac,
+          0x2c3ca9c2,
+          0x2c3d29db,
+          0x2c3da9f8,
+          0x2c3e2a09,
+          0x2c3eaa17,
+          0x2c3f2a2f,
+          0x2c3faa47,
+          0x2c402a54,
           0x2c4090e7,
-          0x2c4129ed,
-          0x2c41aa00,
+          0x2c412a65,
+          0x2c41aa78,
           0x2c4210c0,
-          0x2c42aa11,
+          0x2c42aa89,
           0x2c430720,
-          0x2c43a912,
+          0x2c43a98a,
           0x30320000,
           0x30328015,
           0x3033001f,
@@ -576,169 +576,174 @@
           0x403b9861,
           0x403c0064,
           0x403c8083,
-          0x403d186d,
-          0x403d9883,
-          0x403e1892,
-          0x403e98a5,
-          0x403f18bf,
-          0x403f98cd,
-          0x404018e2,
-          0x404098f6,
-          0x40411913,
-          0x4041992e,
-          0x40421947,
-          0x4042995a,
-          0x4043196e,
-          0x40439986,
-          0x4044199d,
+          0x403d1890,
+          0x403d98a6,
+          0x403e18b5,
+          0x403e98c8,
+          0x403f18e2,
+          0x403f98f0,
+          0x40401905,
+          0x40409919,
+          0x40411936,
+          0x40419951,
+          0x4042196a,
+          0x4042997d,
+          0x40431991,
+          0x404399a9,
+          0x404419c0,
           0x404480ac,
-          0x404519b2,
-          0x404599c4,
-          0x404619e8,
-          0x40469a08,
-          0x40471a16,
-          0x40479a3d,
-          0x40481a52,
-          0x40489a6b,
-          0x40491a82,
-          0x40499a9c,
-          0x404a1ab3,
-          0x404a9ad1,
-          0x404b1ae9,
-          0x404b9b00,
-          0x404c1b16,
-          0x404c9b28,
-          0x404d1b49,
-          0x404d9b6b,
-          0x404e1b7f,
-          0x404e9b8c,
-          0x404f1ba3,
-          0x404f9bb3,
-          0x40501bdd,
-          0x40509bf1,
-          0x40511c0c,
-          0x40519c1c,
-          0x40521c33,
-          0x40529c45,
-          0x40531c5d,
-          0x40539c70,
-          0x40541c85,
-          0x40549ca8,
-          0x40551cb6,
-          0x40559cd3,
-          0x40561ce0,
-          0x40569cf9,
-          0x40571d11,
-          0x40579d24,
-          0x40581d39,
-          0x40589d4b,
-          0x40591d7a,
-          0x40599d93,
-          0x405a1da7,
-          0x405a9db7,
-          0x405b1dcf,
-          0x405b9de0,
-          0x405c1df3,
-          0x405c9e04,
-          0x405d1e11,
-          0x405d9e28,
-          0x405e1e48,
+          0x404519d5,
+          0x404599e7,
+          0x40461a0b,
+          0x40469a2b,
+          0x40471a39,
+          0x40479a60,
+          0x40481a89,
+          0x40489aa2,
+          0x40491ab9,
+          0x40499ad3,
+          0x404a1aea,
+          0x404a9b08,
+          0x404b1b20,
+          0x404b9b37,
+          0x404c1b4d,
+          0x404c9b5f,
+          0x404d1b80,
+          0x404d9ba2,
+          0x404e1bb6,
+          0x404e9bc3,
+          0x404f1bf0,
+          0x404f9c19,
+          0x40501c43,
+          0x40509c57,
+          0x40511c72,
+          0x40519c82,
+          0x40521c99,
+          0x40529cbd,
+          0x40531cd5,
+          0x40539ce8,
+          0x40541cfd,
+          0x40549d20,
+          0x40551d2e,
+          0x40559d4b,
+          0x40561d58,
+          0x40569d71,
+          0x40571d89,
+          0x40579d9c,
+          0x40581db1,
+          0x40589dc3,
+          0x40591df2,
+          0x40599e0b,
+          0x405a1e1f,
+          0x405a9e2f,
+          0x405b1e47,
+          0x405b9e58,
+          0x405c1e6b,
+          0x405c9e7c,
+          0x405d1e89,
+          0x405d9ea0,
+          0x405e1ec0,
           0x405e8a95,
-          0x405f1e69,
-          0x405f9e76,
-          0x40601e84,
-          0x40609ea6,
-          0x40611ece,
-          0x40619ee3,
-          0x40621efa,
-          0x40629f0b,
-          0x40631f1c,
-          0x40639f31,
-          0x40641f48,
-          0x40649f59,
-          0x40651f74,
-          0x40659f8b,
-          0x40661fa3,
-          0x40669fcd,
-          0x40671ff8,
-          0x4067a019,
-          0x4068202c,
-          0x4068a04d,
-          0x4069207f,
-          0x4069a0ad,
-          0x406a20ce,
-          0x406aa0ee,
-          0x406b2276,
-          0x406ba299,
-          0x406c22af,
-          0x406ca4db,
-          0x406d250a,
-          0x406da532,
-          0x406e254b,
-          0x406ea563,
-          0x406f2582,
-          0x406fa597,
-          0x407025aa,
-          0x4070a5c7,
+          0x405f1ee1,
+          0x405f9eee,
+          0x40601efc,
+          0x40609f1e,
+          0x40611f46,
+          0x40619f5b,
+          0x40621f72,
+          0x40629f83,
+          0x40631f94,
+          0x40639fa9,
+          0x40641fc0,
+          0x40649fd1,
+          0x40651fec,
+          0x4065a003,
+          0x4066201b,
+          0x4066a045,
+          0x40672070,
+          0x4067a091,
+          0x406820a4,
+          0x4068a0c5,
+          0x406920f7,
+          0x4069a125,
+          0x406a2146,
+          0x406aa166,
+          0x406b22ee,
+          0x406ba311,
+          0x406c2327,
+          0x406ca553,
+          0x406d2582,
+          0x406da5aa,
+          0x406e25c3,
+          0x406ea5db,
+          0x406f25fa,
+          0x406fa60f,
+          0x40702622,
+          0x4070a63f,
           0x40710800,
-          0x4071a5d9,
-          0x407225ec,
-          0x4072a605,
-          0x4073261d,
+          0x4071a651,
+          0x40722664,
+          0x4072a67d,
+          0x40732695,
           0x4073936d,
-          0x40742631,
-          0x4074a64b,
-          0x4075265c,
-          0x4075a670,
-          0x4076267e,
+          0x407426a9,
+          0x4074a6c3,
+          0x407526d4,
+          0x4075a6e8,
+          0x407626f6,
           0x407691aa,
-          0x407726a3,
-          0x4077a6c5,
-          0x407826e0,
-          0x4078a719,
-          0x40792730,
-          0x4079a746,
-          0x407a2752,
-          0x407aa765,
-          0x407b277a,
-          0x407ba78c,
-          0x407c27a1,
-          0x407ca7aa,
-          0x407d2068,
-          0x407d9bc3,
-          0x407e26f5,
-          0x407e9d5b,
-          0x407f1a2a,
-          0x41f421a1,
-          0x41f92233,
-          0x41fe2126,
-          0x41fea302,
-          0x41ff23f3,
-          0x420321ba,
-          0x420821dc,
-          0x4208a218,
-          0x4209210a,
-          0x4209a252,
-          0x420a2161,
-          0x420aa141,
-          0x420b2181,
-          0x420ba1fa,
-          0x420c240f,
-          0x420ca2cf,
-          0x420d22e9,
-          0x420da320,
-          0x4212233a,
-          0x421723d6,
-          0x4217a37c,
-          0x421c239e,
-          0x421f2359,
-          0x42212426,
-          0x422623b9,
-          0x422b24bf,
-          0x422ba488,
-          0x422c24a7,
-          0x422ca462,
-          0x422d2441,
+          0x4077271b,
+          0x4077a73d,
+          0x40782758,
+          0x4078a791,
+          0x407927a8,
+          0x4079a7be,
+          0x407a27ca,
+          0x407aa7dd,
+          0x407b27f2,
+          0x407ba804,
+          0x407c2819,
+          0x407ca822,
+          0x407d20e0,
+          0x407d9c29,
+          0x407e276d,
+          0x407e9dd3,
+          0x407f1a4d,
+          0x407f986d,
+          0x40801c00,
+          0x40809a75,
+          0x40811cab,
+          0x40819bda,
+          0x41f42219,
+          0x41f922ab,
+          0x41fe219e,
+          0x41fea37a,
+          0x41ff246b,
+          0x42032232,
+          0x42082254,
+          0x4208a290,
+          0x42092182,
+          0x4209a2ca,
+          0x420a21d9,
+          0x420aa1b9,
+          0x420b21f9,
+          0x420ba272,
+          0x420c2487,
+          0x420ca347,
+          0x420d2361,
+          0x420da398,
+          0x421223b2,
+          0x4217244e,
+          0x4217a3f4,
+          0x421c2416,
+          0x421f23d1,
+          0x4221249e,
+          0x42262431,
+          0x422b2537,
+          0x422ba500,
+          0x422c251f,
+          0x422ca4da,
+          0x422d24b9,
           0x4432072b,
           0x4432873a,
           0x44330746,
@@ -781,69 +786,69 @@
           0x4c3d136d,
           0x4c3d937c,
           0x4c3e1389,
-          0x50322a23,
-          0x5032aa32,
-          0x50332a3d,
-          0x5033aa4d,
-          0x50342a66,
-          0x5034aa80,
-          0x50352a8e,
-          0x5035aaa4,
-          0x50362ab6,
-          0x5036aacc,
-          0x50372ae5,
-          0x5037aaf8,
-          0x50382b10,
-          0x5038ab21,
-          0x50392b36,
-          0x5039ab4a,
-          0x503a2b6a,
-          0x503aab80,
-          0x503b2b98,
-          0x503babaa,
-          0x503c2bc6,
-          0x503cabdd,
-          0x503d2bf6,
-          0x503dac0c,
-          0x503e2c19,
-          0x503eac2f,
-          0x503f2c41,
+          0x50322a9b,
+          0x5032aaaa,
+          0x50332ab5,
+          0x5033aac5,
+          0x50342ade,
+          0x5034aaf8,
+          0x50352b06,
+          0x5035ab1c,
+          0x50362b2e,
+          0x5036ab44,
+          0x50372b5d,
+          0x5037ab70,
+          0x50382b88,
+          0x5038ab99,
+          0x50392bae,
+          0x5039abc2,
+          0x503a2be2,
+          0x503aabf8,
+          0x503b2c10,
+          0x503bac22,
+          0x503c2c3e,
+          0x503cac55,
+          0x503d2c6e,
+          0x503dac84,
+          0x503e2c91,
+          0x503eaca7,
+          0x503f2cb9,
           0x503f8382,
-          0x50402c54,
-          0x5040ac64,
-          0x50412c7e,
-          0x5041ac8d,
-          0x50422ca7,
-          0x5042acc4,
-          0x50432cd4,
-          0x5043ace4,
-          0x50442cf3,
+          0x50402ccc,
+          0x5040acdc,
+          0x50412cf6,
+          0x5041ad05,
+          0x50422d1f,
+          0x5042ad3c,
+          0x50432d4c,
+          0x5043ad5c,
+          0x50442d6b,
           0x5044843f,
-          0x50452d07,
-          0x5045ad25,
-          0x50462d38,
-          0x5046ad4e,
-          0x50472d60,
-          0x5047ad75,
-          0x50482d9b,
-          0x5048ada9,
-          0x50492dbc,
-          0x5049add1,
-          0x504a2de7,
-          0x504aadf7,
-          0x504b2e17,
-          0x504bae2a,
-          0x504c2e4d,
-          0x504cae7b,
-          0x504d2e8d,
-          0x504daeaa,
-          0x504e2ec5,
-          0x504eaee1,
-          0x504f2ef3,
-          0x504faf0a,
-          0x50502f19,
+          0x50452d7f,
+          0x5045ad9d,
+          0x50462db0,
+          0x5046adc6,
+          0x50472dd8,
+          0x5047aded,
+          0x50482e13,
+          0x5048ae21,
+          0x50492e34,
+          0x5049ae49,
+          0x504a2e5f,
+          0x504aae6f,
+          0x504b2e8f,
+          0x504baea2,
+          0x504c2ec5,
+          0x504caef3,
+          0x504d2f05,
+          0x504daf22,
+          0x504e2f3d,
+          0x504eaf59,
+          0x504f2f6b,
+          0x504faf82,
+          0x50502f91,
           0x505086ef,
-          0x50512f2c,
+          0x50512fa4,
           0x58320ec9,
           0x68320e8b,
           0x68328c25,
@@ -1204,6 +1209,7 @@
           "BAD_SSL_FILETYPE\\0"
           "BAD_WRITE_RETRY\\0"
           "BIO_NOT_SET\\0"
+          "BUFFERED_MESSAGES_ON_CIPHER_CHANGE\\0"
           "CA_DN_LENGTH_MISMATCH\\0"
           "CA_DN_TOO_LONG\\0"
           "CCS_RECEIVED_EARLY\\0"
@@ -1226,6 +1232,7 @@
           "DIGEST_CHECK_FAILED\\0"
           "DOWNGRADE_DETECTED\\0"
           "DTLS_MESSAGE_TOO_BIG\\0"
+          "DUPLICATE_EXTENSION\\0"
           "ECC_CERT_NOT_FOR_SIGNING\\0"
           "EMS_STATE_INCONSISTENT\\0"
           "ENCRYPTED_LENGTH_TOO_LONG\\0"
@@ -1240,7 +1247,9 @@
           "HTTPS_PROXY_REQUEST\\0"
           "HTTP_REQUEST\\0"
           "INAPPROPRIATE_FALLBACK\\0"
+          "INVALID_ALPN_PROTOCOL\\0"
           "INVALID_COMMAND\\0"
+          "INVALID_COMPRESSION_LIST\\0"
           "INVALID_MESSAGE\\0"
           "INVALID_OUTER_RECORD_TYPE\\0"
           "INVALID_SSL_SESSION\\0"
@@ -1248,6 +1257,7 @@
           "LENGTH_MISMATCH\\0"
           "LIBRARY_HAS_NO_CIPHERS\\0"
           "MISSING_EXTENSION\\0"
+          "MISSING_KEY_SHARE\\0"
           "MISSING_RSA_CERTIFICATE\\0"
           "MISSING_TMP_DH_KEY\\0"
           "MISSING_TMP_ECDH_KEY\\0"
diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h
index 646bf43..4a3f3fa 100644
--- a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h
+++ b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h
@@ -43,7 +43,10 @@
  * Use the provided @c userAgentPrefix at the beginning of the HTTP User Agent string for all calls
  * to the specified @c host.
  */
-+ (void)setUserAgentPrefix:(NSString *)userAgentPrefix forHost:(NSString *)host;
++ (void)setUserAgentPrefix:(nonnull NSString *)userAgentPrefix forHost:(nonnull NSString *)host;
+
+/** The default response size limit is 4MB. Set this to override that default. */
++ (void)setResponseSizeLimit:(NSUInteger)limit forHost:(nonnull NSString *)host;
 
 + (void)closeOpenConnections DEPRECATED_MSG_ATTRIBUTE("The API for this feature is experimental, "
                                                       "and might be removed or modified at any "
diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m
index bcc3b91..7fab357 100644
--- a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m
+++ b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m
@@ -37,15 +37,16 @@
 
 @implementation GRPCCall (ChannelArg)
 
-+ (void)setUserAgentPrefix:(NSString *)userAgentPrefix forHost:(NSString *)host {
-  if (!host) {
-    [NSException raise:NSInvalidArgumentException
-                format:@"host and userAgentPrefix must be provided."];
-  }
++ (void)setUserAgentPrefix:(nonnull NSString *)userAgentPrefix forHost:(nonnull NSString *)host {
   GRPCHost *hostConfig = [GRPCHost hostWithAddress:host];
   hostConfig.userAgentPrefix = userAgentPrefix;
 }
 
++ (void)setResponseSizeLimit:(NSUInteger)limit forHost:(nonnull NSString *)host {
+  GRPCHost *hostConfig = [GRPCHost hostWithAddress:host];
+  hostConfig.responseSizeLimitOverride = @(limit);
+}
+
 + (void)closeOpenConnections {
   [GRPCHost flushChannelCache];
 }
diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.h b/src/objective-c/GRPCClient/GRPCCall+Tests.h
index ccc5723..184ad09 100644
--- a/src/objective-c/GRPCClient/GRPCCall+Tests.h
+++ b/src/objective-c/GRPCClient/GRPCCall+Tests.h
@@ -57,4 +57,10 @@
  * more than one invocation of the methods of this category.
  */
 + (void)useInsecureConnectionsForHost:(NSString *)host;
+
+/**
+ * Resets all host configurations to their default values, and flushes all connections from the
+ * cache.
+ */
++ (void)resetHostSettings;
 @end
diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.m b/src/objective-c/GRPCClient/GRPCCall+Tests.m
index b945669..656cba8 100644
--- a/src/objective-c/GRPCClient/GRPCCall+Tests.m
+++ b/src/objective-c/GRPCClient/GRPCCall+Tests.m
@@ -61,4 +61,7 @@
   hostConfig.secure = NO;
 }
 
++ (void)resetHostSettings {
+  [GRPCHost resetAllHostSettings];
+}
 @end
diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m
index 05a1d10..eecda4c 100644
--- a/src/objective-c/GRPCClient/GRPCCall.m
+++ b/src/objective-c/GRPCClient/GRPCCall.m
@@ -103,6 +103,11 @@
 
 @synthesize state = _state;
 
+// TODO(jcanizales): If grpc_init is idempotent, this should be changed from load to initialize.
++ (void)load {
+  grpc_init();
+}
+
 - (instancetype)init {
   return [self initWithHost:nil path:nil requestsWriter:nil];
 }
diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCChannel.h
index 40e78a9..5bada2d 100644
--- a/src/objective-c/GRPCClient/private/GRPCChannel.h
+++ b/src/objective-c/GRPCClient/private/GRPCChannel.h
@@ -59,8 +59,8 @@
  * Creates a secure channel to the specified @c host using Cronet as a transport mechanism.
  */
 #ifdef GRPC_COMPILE_WITH_CRONET
-+ (nullable GRPCChannel *)secureCronetChannelWithHost:(NSString *)host
-                                          channelArgs:(NSDictionary *)channelArgs;
++ (nullable GRPCChannel *)secureCronetChannelWithHost:(nonnull NSString *)host
+                                          channelArgs:(nonnull NSDictionary *)channelArgs;
 #endif
 /**
  * Creates a secure channel to the specified @c host using the specified @c credentials and
diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCChannel.m
index 7b7b79e..e49acee 100644
--- a/src/objective-c/GRPCClient/private/GRPCChannel.m
+++ b/src/objective-c/GRPCClient/private/GRPCChannel.m
@@ -47,7 +47,7 @@
 #endif
 #import "GRPCCompletionQueue.h"
 
-void freeChannelArgs(grpc_channel_args *channel_args) {
+static void FreeChannelArgs(grpc_channel_args *channel_args) {
   for (size_t i = 0; i < channel_args->num_args; ++i) {
     grpc_arg *arg = &channel_args->args[i];
     gpr_free(arg->key);
@@ -65,7 +65,7 @@
  * value responds to @c @selector(intValue). Otherwise, an exception will be raised. The caller of
  * this function is responsible for calling @c freeChannelArgs on a non-NULL returned value.
  */
-grpc_channel_args * buildChannelArgs(NSDictionary *dictionary) {
+static grpc_channel_args *BuildChannelArgs(NSDictionary *dictionary) {
   if (!dictionary) {
     return NULL;
   }
@@ -115,10 +115,12 @@
   }
 
   if (self = [super init]) {
-    _channelArgs = buildChannelArgs(channelArgs);
+    _channelArgs = BuildChannelArgs(channelArgs);
     _host = [host copy];
-    _unmanagedChannel = grpc_cronet_secure_channel_create(cronetEngine, _host.UTF8String, _channelArgs,
-                                                     NULL);
+    _unmanagedChannel = grpc_cronet_secure_channel_create(cronetEngine,
+                                                          _host.UTF8String,
+                                                          _channelArgs,
+                                                          NULL);
   }
 
   return self;
@@ -138,7 +140,7 @@
   }
 
   if (self = [super init]) {
-    _channelArgs = buildChannelArgs(channelArgs);
+    _channelArgs = BuildChannelArgs(channelArgs);
     _host = [host copy];
     if (secure) {
       _unmanagedChannel = grpc_secure_channel_create(credentials, _host.UTF8String, _channelArgs,
@@ -155,7 +157,7 @@
   // TODO(jcanizales): Be sure to add a test with a server that closes the connection prematurely,
   // as in the past that made this call to crash.
   grpc_channel_destroy(_unmanagedChannel);
-  freeChannelArgs(_channelArgs);
+  FreeChannelArgs(_channelArgs);
 }
 
 #ifdef GRPC_COMPILE_WITH_CRONET
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.h b/src/objective-c/GRPCClient/private/GRPCHost.h
index 350c69b..c8b5dd3 100644
--- a/src/objective-c/GRPCClient/private/GRPCHost.h
+++ b/src/objective-c/GRPCClient/private/GRPCHost.h
@@ -42,6 +42,7 @@
 @interface GRPCHost : NSObject
 
 + (void)flushChannelCache;
++ (void)resetAllHostSettings;
 
 @property(nonatomic, readonly) NSString *address;
 @property(nonatomic, copy, nullable) NSString *userAgentPrefix;
@@ -53,6 +54,10 @@
 
 @property(nonatomic, copy, nullable) NSString *hostNameOverride;
 
+/** The default response size limit is 4MB. Set this to override that default. */
+@property(nonatomic, strong, nullable) NSNumber *responseSizeLimitOverride;
+
+
 - (nullable instancetype)init NS_UNAVAILABLE;
 /** Host objects initialized with the same address are the same. */
 + (nullable instancetype)hostWithAddress:(NSString *)address;
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m
index 08c699f..f8634b4 100644
--- a/src/objective-c/GRPCClient/private/GRPCHost.m
+++ b/src/objective-c/GRPCClient/private/GRPCHost.m
@@ -49,7 +49,7 @@
 
 // TODO(jcanizales): Generate the version in a standalone header, from templates. Like
 // templates/src/core/surface/version.c.template .
-#define GRPC_OBJC_VERSION_STRING @"0.13.0"
+#define GRPC_OBJC_VERSION_STRING @"1.0.0"
 
 static NSMutableDictionary *kHostCache;
 
@@ -113,6 +113,12 @@
   }
 }
 
++ (void)resetAllHostSettings {
+  @synchronized (kHostCache) {
+    kHostCache = [NSMutableDictionary dictionary];
+  }
+}
+
 - (nullable grpc_call *)unmanagedCallWithPath:(NSString *)path
                               completionQueue:(GRPCCompletionQueue *)queue {
   GRPCChannel *channel;
@@ -209,6 +215,12 @@
   if (_secure && _hostNameOverride) {
     args[@GRPC_SSL_TARGET_NAME_OVERRIDE_ARG] = _hostNameOverride;
   }
+
+  if (_responseSizeLimitOverride) {
+    args[@GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH] = _responseSizeLimitOverride;
+  }
+  // Use 10000ms initial backoff time for correct behavior on bad/slow networks  
+  args[@GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS] = @10000;
   return args;
 }
 
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
index 97f6b89..1339429 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
@@ -245,11 +245,6 @@
   }
 
   if (self = [super init]) {
-    static dispatch_once_t initialization;
-    dispatch_once(&initialization, ^{
-      grpc_init();
-    });
-
     // Each completion queue consumes one thread. There's a trade to be made between creating and
     // consuming too many threads and having contention of multiple calls in a single completion
     // queue. Currently we use a singleton queue.
diff --git a/src/objective-c/README.md b/src/objective-c/README.md
index 6e917dd..3624475 100644
--- a/src/objective-c/README.md
+++ b/src/objective-c/README.md
@@ -48,7 +48,7 @@
   src = '.'
 
   # We'll use protoc with the gRPC plugin.
-  s.dependency '!ProtoCompiler-gRPCPlugin', '~> 1.0.0-pre1'
+  s.dependency '!ProtoCompiler-gRPCPlugin', '~> 1.0'
 
   # Pods directory corresponding to this app's Podfile, relative to the location of this podspec.
   pods_root = '<path to your Podfile>/Pods'
@@ -82,10 +82,6 @@
     ms.requires_arc = false
     # The generated files depend on the protobuf runtime.
     ms.dependency 'Protobuf'
-    # This is needed by all pods that depend on Protobuf:
-    ms.pod_target_xcconfig = {
-      'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
-    }
   end
 
   # The --objcgrpc_out plugin generates a pair of .pbrpc.h/.pbrpc.m files for each .proto file with
@@ -98,6 +94,13 @@
     ss.dependency 'gRPC-ProtoRPC'
     ss.dependency "#{s.name}/Messages"
   end
+
+  s.pod_target_xcconfig = {
+    # This is needed by all pods that depend on Protobuf:
+    'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
+    # This is needed by all pods that depend on gRPC-RxLibrary:
+    'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
+  }
 end
 ```
 
diff --git a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m
index f3242e4..6ee1545 100644
--- a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m
+++ b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m
@@ -33,10 +33,6 @@
 
 #import "GRXMappingWriter.h"
 
-static id (^kIdentity)(id value) = ^id(id value) {
-  return value;
-};
-
 @interface GRXForwardingWriter () <GRXWriteable>
 @end
 
@@ -51,7 +47,9 @@
 // Designated initializer
 - (instancetype)initWithWriter:(GRXWriter *)writer map:(id (^)(id value))map {
   if ((self = [super initWithWriter:writer])) {
-    _map = map ?: kIdentity;
+    _map = map ?: ^id(id value) {
+      return value;
+    };
   }
   return self;
 }
diff --git a/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec b/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec
index 7222a80..ea61813 100644
--- a/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec
+++ b/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec
@@ -11,7 +11,7 @@
   s.osx.deployment_target = '10.9'
 
   # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
-  s.dependency "!ProtoCompiler-gRPCPlugin", "~> 1.0.0-pre1"
+  s.dependency "!ProtoCompiler-gRPCPlugin"
 
   repo_root = '../../../..'
   bin_dir = "#{repo_root}/bins/$CONFIG"
@@ -35,10 +35,6 @@
     ms.header_mappings_dir = '.'
     ms.requires_arc = false
     ms.dependency 'Protobuf'
-    # This is needed by all pods that depend on Protobuf:
-    ms.pod_target_xcconfig = {
-      'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
-    }
   end
 
   s.subspec 'Services' do |ss|
@@ -48,4 +44,11 @@
     ss.dependency 'gRPC-ProtoRPC'
     ss.dependency "#{s.name}/Messages"
   end
+
+  s.pod_target_xcconfig = {
+    # This is needed by all pods that depend on Protobuf:
+    'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
+    # This is needed by all pods that depend on gRPC-RxLibrary:
+    'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
+  }
 end
diff --git a/src/objective-c/examples/SwiftSample/ViewController.swift b/src/objective-c/examples/SwiftSample/ViewController.swift
index e7bab13..66d4fa9 100644
--- a/src/objective-c/examples/SwiftSample/ViewController.swift
+++ b/src/objective-c/examples/SwiftSample/ViewController.swift
@@ -91,7 +91,7 @@
 
     call.startWithWriteable(GRXWriteable { response, error in
       if let response = response as? NSData {
-        NSLog("3. Received response:\n\(RMTSimpleResponse(data: response, error: nil))")
+        NSLog("3. Received response:\n\(try! RMTSimpleResponse(data: response))")
       } else {
         NSLog("3. Finished with error: \(error!)")
       }
diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m
deleted file mode 100644
index 58abb49..0000000
--- a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-/*
- * This test file is derived from fixture h2_ssl.c in core end2end test
- * (test/core/end2end/fixture/h2_ssl.c). The structure of the fixture is
- * preserved as much as possible
- *
- * This fixture creates a server full stack using chttp2 and a client
- * full stack using Cronet. End-to-end tests are run against this
- * configuration
- *
- */
-
-
-#import <XCTest/XCTest.h>
-#include "test/core/end2end/end2end_tests.h"
-
-#include <stdio.h>
-#include <string.h>
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/host_port.h>
-#include <grpc/support/log.h>
-
-#include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/security/credentials/credentials.h"
-#include "src/core/lib/support/env.h"
-#include "src/core/lib/support/string.h"
-#include "src/core/lib/support/tmpfile.h"
-#include "test/core/end2end/data/ssl_test_data.h"
-#include "test/core/util/port.h"
-#include "test/core/util/test_config.h"
-
-#include <grpc/grpc_cronet.h>
-#import <Cronet/Cronet.h>
-
-typedef struct fullstack_secure_fixture_data {
-  char *localaddr;
-} fullstack_secure_fixture_data;
-
-static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack(
-                                                                        grpc_channel_args *client_args, grpc_channel_args *server_args) {
-  grpc_end2end_test_fixture f;
-  int port = grpc_pick_unused_port_or_die();
-  fullstack_secure_fixture_data *ffd =
-  gpr_malloc(sizeof(fullstack_secure_fixture_data));
-  memset(&f, 0, sizeof(f));
-  
-  gpr_join_host_port(&ffd->localaddr, "localhost", port);
-  
-  f.fixture_data = ffd;
-  f.cq = grpc_completion_queue_create(NULL);
-  
-  return f;
-}
-
-static void process_auth_failure(void *state, grpc_auth_context *ctx,
-                                 const grpc_metadata *md, size_t md_count,
-                                 grpc_process_auth_metadata_done_cb cb,
-                                 void *user_data) {
-  GPR_ASSERT(state == NULL);
-  cb(user_data, NULL, 0, NULL, 0, GRPC_STATUS_UNAUTHENTICATED, NULL);
-}
-
-static void cronet_init_client_secure_fullstack(
-                                                grpc_end2end_test_fixture *f, grpc_channel_args *client_args,
-                                                cronet_engine *cronetEngine) {
-  fullstack_secure_fixture_data *ffd = f->fixture_data;
-  f->client =
-  grpc_cronet_secure_channel_create(cronetEngine, ffd->localaddr, client_args, NULL);
-  GPR_ASSERT(f->client != NULL);
-}
-
-static void chttp2_init_server_secure_fullstack(
-                                                grpc_end2end_test_fixture *f, grpc_channel_args *server_args,
-                                                grpc_server_credentials *server_creds) {
-  fullstack_secure_fixture_data *ffd = f->fixture_data;
-  if (f->server) {
-    grpc_server_destroy(f->server);
-  }
-  f->server = grpc_server_create(server_args, NULL);
-  grpc_server_register_completion_queue(f->server, f->cq, NULL);
-  GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr,
-                                               server_creds));
-  grpc_server_credentials_release(server_creds);
-  grpc_server_start(f->server);
-}
-
-static void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) {
-  fullstack_secure_fixture_data *ffd = f->fixture_data;
-  gpr_free(ffd->localaddr);
-  gpr_free(ffd);
-}
-
-static void cronet_init_client_simple_ssl_secure_fullstack(
-                                                           grpc_end2end_test_fixture *f, grpc_channel_args *client_args) {
-  grpc_arg ssl_name_override = {GRPC_ARG_STRING,
-    GRPC_SSL_TARGET_NAME_OVERRIDE_ARG,
-    {"foo.test.google.fr"}};
-  
-  grpc_channel_args *new_client_args =
-  grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1);
-  [Cronet setHttp2Enabled:YES];
-  [Cronet start];
-  cronet_engine *cronetEngine = [Cronet getGlobalEngine];
-  
-  cronet_init_client_secure_fullstack(f, new_client_args, cronetEngine);
-  grpc_channel_args_destroy(new_client_args);
-}
-
-static int fail_server_auth_check(grpc_channel_args *server_args) {
-  size_t i;
-  if (server_args == NULL) return 0;
-  for (i = 0; i < server_args->num_args; i++) {
-    if (strcmp(server_args->args[i].key, FAIL_AUTH_CHECK_SERVER_ARG_NAME) ==
-        0) {
-      return 1;
-    }
-  }
-  return 0;
-}
-
-static void chttp2_init_server_simple_ssl_secure_fullstack(
-                                                           grpc_end2end_test_fixture *f, grpc_channel_args *server_args) {
-  grpc_ssl_pem_key_cert_pair pem_cert_key_pair = {test_server1_key,
-    test_server1_cert};
-  grpc_server_credentials *ssl_creds =
-  grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0, NULL);
-  if (fail_server_auth_check(server_args)) {
-    grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL};
-    grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor);
-  }
-  chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);
-}
-
-/* All test configurations */
-
-static grpc_end2end_test_config configs[] = {
-  {"chttp2/simple_ssl_fullstack",
-    FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
-    FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS,
-    chttp2_create_fixture_secure_fullstack,
-    cronet_init_client_simple_ssl_secure_fullstack,
-    chttp2_init_server_simple_ssl_secure_fullstack,
-    chttp2_tear_down_secure_fullstack},
-};
-
-
-
-static char *roots_filename;
-
-@interface CoreCronetEnd2EndTests : XCTestCase
-
-@end
-
-@implementation CoreCronetEnd2EndTests
-
-
-// The setUp() function is run before the test cases run and only run once
-+ (void)setUp {
-  [super setUp];
-
-  FILE *roots_file;
-  size_t roots_size = strlen(test_root_cert);
-  
-  char *argv[] = {"CoreCronetEnd2EndTests"};
-  grpc_test_init(1, argv);
-  grpc_end2end_tests_pre_init();
-  
-  /* Set the SSL roots env var. */
-  roots_file = gpr_tmpfile("chttp2_simple_ssl_fullstack_test", &roots_filename);
-  GPR_ASSERT(roots_filename != NULL);
-  GPR_ASSERT(roots_file != NULL);
-  GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size);
-  fclose(roots_file);
-  gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename);
-  
-  grpc_init();
-  
-}
-
-// The tearDown() function is run after all test cases finish running
-+ (void)tearDown {
-  grpc_shutdown();
-  
-  /* Cleanup. */
-  remove(roots_filename);
-  gpr_free(roots_filename);
-  
-  [super tearDown];
-}
-
-- (void)testIndividualCase:(char*)test_case {
-  char *argv[] = {"h2_ssl", test_case};
-  
-  for (int i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
-    grpc_end2end_tests(sizeof(argv) / sizeof(argv[0]), argv, configs[i]);
-  }
-}
-
-// TODO(mxyan): Use NSStringFromSelector(_cmd) to acquire test name from the
-// test case method name, so that bodies of test cases can stay identical 
-- (void)testBadHostname {
-  [self testIndividualCase:"bad_hostname"];
-}
-
-- (void)testBinaryMetadata {
-  [self testIndividualCase:"binary_metadata"];
-}
-
-- (void)testCallCreds {
-  [self testIndividualCase:"call_creds"];
-}
-
-- (void)testCancelAfterAccept {
-  [self testIndividualCase:"cancel_after_accept"];
-}
-
-- (void)testCancelAfterClientDone {
-  [self testIndividualCase:"cancel_after_client_done"];
-}
-
-- (void)testCancelAfterInvoke {
-  [self testIndividualCase:"cancel_after_invoke"];
-}
-
-- (void)testCancelBeforeInvoke {
-  [self testIndividualCase:"cancel_before_invoke"];
-}
-
-- (void)testCancelInAVacuum {
-  [self testIndividualCase:"cancel_in_a_vacuum"];
-}
-
-- (void)testCancelWithStatus {
-  [self testIndividualCase:"cancel_with_status"];
-}
-
-- (void)testCompressedPayload {
-  [self testIndividualCase:"compressed_payload"];
-}
-
-- (void)testConnectivity {
-  [self testIndividualCase:"connectivity"];
-}
-
-- (void)testDefaultHost {
-  [self testIndividualCase:"default_host"];
-}
-
-- (void)testDisappearingServer {
-  [self testIndividualCase:"disappearing_server"];
-}
-
-- (void)testEmptyBatch {
-  [self testIndividualCase:"empty_batch"];
-}
-
-- (void)testFilterCausesClose {
-  [self testIndividualCase:"filter_causes_close"];
-}
-
-- (void)testGracefulServerShutdown {
-  [self testIndividualCase:"graceful_server_shutdown"];
-}
-
-- (void)testHighInitialSeqno {
-  [self testIndividualCase:"high_initial_seqno"];
-}
-
-- (void)testHpackSize {
-  [self testIndividualCase:"hpack_size"];
-}
-
-- (void)testIdempotentRequest {
-  [self testIndividualCase:"idempotent_request"];
-}
-
-- (void)testInvokeLargeRequest {
-  [self testIndividualCase:"invoke_large_request"];
-}
-
-- (void)testLargeMetadata {
-  [self testIndividualCase:"large_metadata"];
-}
-
-- (void)testMaxConcurrentStreams {
-  [self testIndividualCase:"max_concurrent_streams"];
-}
-
-- (void)testMaxMessageLength {
-  [self testIndividualCase:"max_message_length"];
-}
-
-- (void)testNegativeDeadline {
-  [self testIndividualCase:"negative_deadline"];
-}
-
-- (void)testNetworkStatusChange {
-  [self testIndividualCase:"network_status_change"];
-}
-
-- (void)testNoOp {
-  [self testIndividualCase:"no_op"];
-}
-
-- (void)testPayload {
-  [self testIndividualCase:"payload"];
-}
-
-- (void)testPing {
-  [self testIndividualCase:"ping"];
-}
-
-- (void)testPingPongStreaming {
-  [self testIndividualCase:"ping_pong_streaming"];
-}
-
-- (void)testRegisteredCall {
-  [self testIndividualCase:"registered_call"];
-}
-
-- (void)testRequestWithFlags {
-  [self testIndividualCase:"request_with_flags"];
-}
-
-- (void)testRequestWithPayload {
-  [self testIndividualCase:"request_with_payload"];
-}
-
-- (void)testServerFinishesRequest {
-  [self testIndividualCase:"server_finishes_request"];
-}
-
-- (void)testShutdownFinishesCalls {
-  [self testIndividualCase:"shutdown_finishes_calls"];
-}
-
-- (void)testShutdownFinishesTags {
-  [self testIndividualCase:"shutdown_finishes_tags"];
-}
-
-- (void)testSimpleDelayedRequest {
-  [self testIndividualCase:"simple_delayed_request"];
-}
-
-- (void)testSimpleMetadata {
-  [self testIndividualCase:"simple_metadata"];
-}
-
-- (void)testSimpleRequest {
-  [self testIndividualCase:"simple_request"];
-}
-
-- (void)testStreamingErrorResponse {
-  [self testIndividualCase:"streaming_error_response"];
-}
-
-- (void)testTrailingMetadata {
-  [self testIndividualCase:"trailing_metadata"];
-}
-
-@end
diff --git a/src/objective-c/tests/GRPCClientTests.m b/src/objective-c/tests/GRPCClientTests.m
index 1167a71..916a335 100644
--- a/src/objective-c/tests/GRPCClientTests.m
+++ b/src/objective-c/tests/GRPCClientTests.m
@@ -292,15 +292,6 @@
 
 // TODO(makarandd): Move to a different file that contains only unit tests
 - (void)testExceptions {
-  // Try to set userAgentPrefix for host that is nil. This should cause
-  // an exception.
-  @try {
-    [GRPCCall setUserAgentPrefix:@"Foo" forHost:nil];
-    XCTFail(@"Did not receive an exception when host is nil");
-  } @catch(NSException *theException) {
-    NSLog(@"Received exception as expected: %@", theException.name);
-  }
-
   // Try to set parameters to nil for GRPCCall. This should cause an exception
   @try {
     (void)[[GRPCCall alloc] initWithHost:nil
diff --git a/src/objective-c/tests/InteropTests.h b/src/objective-c/tests/InteropTests.h
index 6d54343..ecab606 100644
--- a/src/objective-c/tests/InteropTests.h
+++ b/src/objective-c/tests/InteropTests.h
@@ -46,4 +46,11 @@
  * Override in a subclass to perform these tests against a specific address.
  */
 + (NSString *)host;
+
+/**
+ * Bytes of overhead of test proto responses due to encoding. This is used to excercise the behavior
+ * when responses are just above or below the max response size. For some reason, the local and
+ * remote servers enconde responses with different overhead (?), so this is defined per-subclass.
+ */
+- (int32_t)encodingOverhead;
 @end
diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m
index 494743d..44f22c9 100644
--- a/src/objective-c/tests/InteropTests.m
+++ b/src/objective-c/tests/InteropTests.m
@@ -80,10 +80,6 @@
 
 #pragma mark Tests
 
-#ifdef GRPC_COMPILE_WITH_CRONET
-static cronet_engine *cronetEngine = NULL;
-#endif
-
 @implementation InteropTests {
   RMTTestService *_service;
 }
@@ -92,15 +88,22 @@
   return nil;
 }
 
+- (int32_t)encodingOverhead {
+  return 0;
+}
+
 - (void)setUp {
+  self.continueAfterFailure = NO;
+
+  [GRPCCall resetHostSettings];
+
   _service = self.class.host ? [RMTTestService serviceWithHost:self.class.host] : nil;
 #ifdef GRPC_COMPILE_WITH_CRONET
   if (cronetEngine == NULL) {
     // Cronet setup
     [Cronet setHttp2Enabled:YES];
     [Cronet start];
-    cronetEngine = [Cronet getGlobalEngine];
-    [GRPCCall useCronetWithEngine:cronetEngine];
+    [GRPCCall useCronetWithEngine:[Cronet getGlobalEngine]];
   }
 #endif
 }
@@ -146,6 +149,64 @@
   [self waitForExpectationsWithTimeout:16 handler:nil];
 }
 
+- (void)test4MBResponsesAreAccepted {
+  XCTAssertNotNil(self.class.host);
+  __weak XCTestExpectation *expectation = [self expectationWithDescription:@"MaxResponseSize"];
+
+  RMTSimpleRequest *request = [RMTSimpleRequest message];
+  const int32_t kPayloadSize = 4 * 1024 * 1024 - self.encodingOverhead; // 4MB - encoding overhead
+  request.responseSize = kPayloadSize;
+
+  [_service unaryCallWithRequest:request handler:^(RMTSimpleResponse *response, NSError *error) {
+    XCTAssertNil(error, @"Finished with unexpected error: %@", error);
+    XCTAssertEqual(response.payload.body.length, kPayloadSize);
+    [expectation fulfill];
+  }];
+
+  [self waitForExpectationsWithTimeout:16 handler:nil];
+}
+
+- (void)testResponsesOverMaxSizeFailWithActionableMessage {
+  XCTAssertNotNil(self.class.host);
+  __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ResponseOverMaxSize"];
+
+  RMTSimpleRequest *request = [RMTSimpleRequest message];
+  const int32_t kPayloadSize = 4 * 1024 * 1024 - self.encodingOverhead + 1; // 1B over max size
+  request.responseSize = kPayloadSize;
+
+  [_service unaryCallWithRequest:request handler:^(RMTSimpleResponse *response, NSError *error) {
+    // TODO(jcanizales): Catch the error and rethrow it with an actionable message:
+    // - Use +[GRPCCall setResponseSizeLimit:forHost:] to set a higher limit.
+    // - If you're developing the server, consider using response streaming, or let clients filter
+    //   responses by setting a google.protobuf.FieldMask in the request:
+    //   https://github.com/google/protobuf/blob/master/src/google/protobuf/field_mask.proto
+    XCTAssertEqualObjects(error.localizedDescription, @"Received message larger than max (4194305 vs. 4194304)");
+    [expectation fulfill];
+  }];
+
+  [self waitForExpectationsWithTimeout:16 handler:nil];
+}
+
+- (void)testResponsesOver4MBAreAcceptedIfOptedIn {
+  XCTAssertNotNil(self.class.host);
+  __weak XCTestExpectation *expectation =
+      [self expectationWithDescription:@"HigherResponseSizeLimit"];
+
+  RMTSimpleRequest *request = [RMTSimpleRequest message];
+  const size_t kPayloadSize = 5 * 1024 * 1024; // 5MB
+  request.responseSize = kPayloadSize;
+
+  [GRPCCall setResponseSizeLimit:6 * 1024 * 1024 forHost:self.class.host];
+
+  [_service unaryCallWithRequest:request handler:^(RMTSimpleResponse *response, NSError *error) {
+    XCTAssertNil(error, @"Finished with unexpected error: %@", error);
+    XCTAssertEqual(response.payload.body.length, kPayloadSize);
+    [expectation fulfill];
+  }];
+
+  [self waitForExpectationsWithTimeout:16 handler:nil];
+}
+
 - (void)testClientStreamingRPC {
   XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ClientStreaming"];
diff --git a/src/objective-c/tests/InteropTestsLocalCleartext.m b/src/objective-c/tests/InteropTestsLocalCleartext.m
index 56927a8..b41210f 100644
--- a/src/objective-c/tests/InteropTestsLocalCleartext.m
+++ b/src/objective-c/tests/InteropTestsLocalCleartext.m
@@ -47,11 +47,15 @@
   return kLocalCleartextHost;
 }
 
+- (int32_t)encodingOverhead {
+  return 10; // bytes
+}
+
 - (void)setUp {
+  [super setUp];
+
   // Register test server as non-SSL.
   [GRPCCall useInsecureConnectionsForHost:kLocalCleartextHost];
-
-  [super setUp];
 }
 
 @end
diff --git a/src/objective-c/tests/InteropTestsLocalSSL.m b/src/objective-c/tests/InteropTestsLocalSSL.m
index f0f4b1d..1479c58 100644
--- a/src/objective-c/tests/InteropTestsLocalSSL.m
+++ b/src/objective-c/tests/InteropTestsLocalSSL.m
@@ -47,14 +47,18 @@
   return kLocalSSLHost;
 }
 
+- (int32_t)encodingOverhead {
+  return 10; // bytes
+}
+
 - (void)setUp {
+  [super setUp];
+
   // Register test server certificates and name.
   NSBundle *bundle = [NSBundle bundleForClass:self.class];
   NSString *certsPath = [bundle pathForResource:@"TestCertificates.bundle/test-certificates"
                                          ofType:@"pem"];
   [GRPCCall useTestCertsPath:certsPath testName:@"foo.test.google.fr" forHost:kLocalSSLHost];
-
-  [super setUp];
 }
 
 - (void)testExceptions {
diff --git a/src/objective-c/tests/InteropTestsRemote.m b/src/objective-c/tests/InteropTestsRemote.m
index 758cc93..70f8475 100644
--- a/src/objective-c/tests/InteropTestsRemote.m
+++ b/src/objective-c/tests/InteropTestsRemote.m
@@ -47,4 +47,8 @@
   return kRemoteSSLHost;
 }
 
+- (int32_t)encodingOverhead {
+  return 12; // bytes
+}
+
 @end
diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile
index 3d0664a..17478fa 100644
--- a/src/objective-c/tests/Podfile
+++ b/src/objective-c/tests/Podfile
@@ -14,6 +14,7 @@
   InteropTestsRemote
   InteropTestsLocalSSL
   InteropTestsLocalCleartext
+  InteropTestsRemoteWithCronet
 ).each do |target_name|
   target target_name do
     pod 'Protobuf', :path => "#{GRPC_LOCAL_SRC}/third_party/protobuf", :inhibit_warnings => true
@@ -28,6 +29,11 @@
     pod 'gRPC-RxLibrary', :path => GRPC_LOCAL_SRC
     pod 'gRPC-ProtoRPC',  :path => GRPC_LOCAL_SRC
     pod 'RemoteTest', :path => "RemoteTestClient"
+
+    if target_name == 'InteropTestsRemoteWithCronet'
+      pod 'gRPC-Core/Cronet-Implementation', :path => GRPC_LOCAL_SRC
+      pod 'CronetFramework', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c"
+    end
   end
 end
 
@@ -36,23 +42,8 @@
   pod 'CronetFramework', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c"
   pod 'gRPC-Core', :path => GRPC_LOCAL_SRC
   pod 'gRPC-Core/Cronet-Interface', :path => GRPC_LOCAL_SRC
-  pod 'gRPC-Core/Cronet-Tests', :path => GRPC_LOCAL_SRC
-end
-
-target 'InteropTestsRemoteWithCronet' do
-  pod 'Protobuf', :path => "#{GRPC_LOCAL_SRC}/third_party/protobuf", :inhibit_warnings => true
-
-  pod '!ProtoCompiler',            :path => "#{GRPC_LOCAL_SRC}/src/objective-c"
-  pod '!ProtoCompiler-gRPCPlugin', :path => "#{GRPC_LOCAL_SRC}/src/objective-c"
-
-  pod 'BoringSSL',       :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true
-  pod 'CronetFramework', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c"
-  
-  pod 'gRPC',           :path => GRPC_LOCAL_SRC
-  pod 'gRPC-Core',      :path => GRPC_LOCAL_SRC
-  pod 'gRPC-RxLibrary', :path => GRPC_LOCAL_SRC
-  pod 'gRPC-ProtoRPC',  :path => GRPC_LOCAL_SRC
-  pod 'RemoteTest', :path => "RemoteTestClient"
+  pod 'gRPC-Core/Cronet-Implementation', :path => GRPC_LOCAL_SRC
+  pod 'gRPC-Core/Tests', :path => GRPC_LOCAL_SRC
 end
 
 # gRPC-Core.podspec needs to be modified to be successfully used for local development. A Podfile's
@@ -91,7 +82,11 @@
     target.build_configurations.each do |config|
       config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'YES'
     end
-    if target.name == 'gRPC-Core' or target.name == 'gRPC-Core.default-Cronet-Interface-Cronet-Tests'
+
+    # CocoaPods creates duplicated library targets of gRPC-Core when the test targets include
+    # non-default subspecs of gRPC-Core. All of these library targets start with prefix 'gRPC-Core.'
+    # and require the same error suppresion.
+    if target.name == 'gRPC-Core' or target.name.start_with?('gRPC-Core.') 
       target.build_configurations.each do |config|
         # TODO(zyc): Remove this setting after the issue is resolved
         # GPR_UNREACHABLE_CODE causes "Control may reach end of non-void
@@ -99,5 +94,15 @@
         config.build_settings['GCC_WARN_ABOUT_RETURN_TYPE'] = 'NO'
       end
     end
+
+    # Activate Cronet for the dedicated build configuration 'Cronet', which will be used solely by
+    # the test target 'InteropTestsRemoteWithCronet'
+    if target.name == 'gRPC'
+      target.build_configurations.each do |config|
+        if config.name == 'Cronet'
+          config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) COCOAPODS=1 GRPC_COMPILE_WITH_CRONET=1'
+        end
+      end
+    end
   end
 end
diff --git a/src/objective-c/tests/RemoteTestClient/RemoteTest.podspec b/src/objective-c/tests/RemoteTestClient/RemoteTest.podspec
index 53ba101..2e0a050 100644
--- a/src/objective-c/tests/RemoteTestClient/RemoteTest.podspec
+++ b/src/objective-c/tests/RemoteTestClient/RemoteTest.podspec
@@ -11,7 +11,7 @@
   s.osx.deployment_target = '10.9'
 
   # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
-  s.dependency "!ProtoCompiler-gRPCPlugin", "~> 1.0.0-pre1"
+  s.dependency "!ProtoCompiler-gRPCPlugin"
 
   repo_root = '../../../..'
   bin_dir = "#{repo_root}/bins/$CONFIG"
@@ -35,10 +35,6 @@
     ms.header_mappings_dir = "."
     ms.requires_arc = false
     ms.dependency "Protobuf"
-    # This is needed by all pods that depend on Protobuf:
-    ms.pod_target_xcconfig = {
-      'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
-    }
   end
 
   s.subspec "Services" do |ss|
@@ -48,4 +44,11 @@
     ss.dependency "gRPC-ProtoRPC"
     ss.dependency "#{s.name}/Messages"
   end
+
+  s.pod_target_xcconfig = {
+    # This is needed by all pods that depend on Protobuf:
+    'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
+    # This is needed by all pods that depend on gRPC-RxLibrary:
+    'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
+  }
 end
diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
index 1d90347..c4a6567 100644
--- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
+++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
@@ -113,13 +113,17 @@
 		07D10A965323BEA7FE59A74B /* Pods-RxLibraryUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxLibraryUnitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RxLibraryUnitTests/Pods-RxLibraryUnitTests.debug.xcconfig"; sourceTree = "<group>"; };
 		0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
 		0D2284C3DF7E57F0ED504E39 /* Pods-CoreCronetEnd2EndTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CoreCronetEnd2EndTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CoreCronetEnd2EndTests/Pods-CoreCronetEnd2EndTests.debug.xcconfig"; sourceTree = "<group>"; };
+		14B09A58FEE53A7A6B838920 /* Pods-InteropTestsLocalSSL.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalSSL.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL.cronet.xcconfig"; sourceTree = "<group>"; };
 		17F60BF2871F6AF85FB3FA12 /* Pods-InteropTestsRemoteWithCronet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemoteWithCronet.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemoteWithCronet/Pods-InteropTestsRemoteWithCronet.debug.xcconfig"; sourceTree = "<group>"; };
 		20DFF2F3C97EF098FE5A3171 /* libPods-Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		3B0861FC805389C52DB260D4 /* Pods-RxLibraryUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxLibraryUnitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RxLibraryUnitTests/Pods-RxLibraryUnitTests.release.xcconfig"; sourceTree = "<group>"; };
+		3F27B2E744482771EB93C394 /* Pods-InteropTestsRemoteWithCronet.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemoteWithCronet.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemoteWithCronet/Pods-InteropTestsRemoteWithCronet.cronet.xcconfig"; sourceTree = "<group>"; };
 		4AD97096D13D7416DC91A72A /* Pods-CoreCronetEnd2EndTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CoreCronetEnd2EndTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CoreCronetEnd2EndTests/Pods-CoreCronetEnd2EndTests.release.xcconfig"; sourceTree = "<group>"; };
+		4ADEA1C8BBE10D90940AC68E /* Pods-InteropTestsRemote.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemote.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemote/Pods-InteropTestsRemote.cronet.xcconfig"; sourceTree = "<group>"; };
 		51A275E86C141416ED63FF76 /* Pods-InteropTestsLocalCleartext.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.release.xcconfig"; sourceTree = "<group>"; };
 		553BBBED24E4162D1F769D65 /* Pods-InteropTestsLocalSSL.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalSSL.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL.debug.xcconfig"; sourceTree = "<group>"; };
+		573450F334B331D0BED8B961 /* Pods-CoreCronetEnd2EndTests.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CoreCronetEnd2EndTests.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-CoreCronetEnd2EndTests/Pods-CoreCronetEnd2EndTests.cronet.xcconfig"; sourceTree = "<group>"; };
 		5761E98978DDDF136A58CB7E /* Pods-AllTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AllTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AllTests/Pods-AllTests.release.xcconfig"; sourceTree = "<group>"; };
 		5E8A5DA41D3840B4000F8BC4 /* CoreCronetEnd2EndTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreCronetEnd2EndTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CoreCronetEnd2EndTests.m; sourceTree = "<group>"; };
@@ -142,9 +146,11 @@
 		63E240CC1B6C4D3A005F3B0E /* InteropTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InteropTests.h; sourceTree = "<group>"; };
 		63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsLocalSSL.m; sourceTree = "<group>"; };
 		63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = TestCertificates.bundle; sourceTree = "<group>"; };
+		79C68EFFCB5533475D810B79 /* Pods-RxLibraryUnitTests.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxLibraryUnitTests.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-RxLibraryUnitTests/Pods-RxLibraryUnitTests.cronet.xcconfig"; sourceTree = "<group>"; };
 		7A2E97E3F469CC2A758D77DE /* Pods-InteropTestsLocalSSL.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalSSL.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL.release.xcconfig"; sourceTree = "<group>"; };
 		9E9444C764F0FFF64A7EB58E /* libPods-InteropTestsRemoteWithCronet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-InteropTestsRemoteWithCronet.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		A58BE6DF1C62D1739EBB2C78 /* libPods-RxLibraryUnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RxLibraryUnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		AA7CB64B4DD9915AE7C03163 /* Pods-InteropTestsLocalCleartext.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.cronet.xcconfig"; sourceTree = "<group>"; };
 		AC414EF7A6BF76ED02B6E480 /* Pods-InteropTestsRemoteWithCronet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemoteWithCronet.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemoteWithCronet/Pods-InteropTestsRemoteWithCronet.release.xcconfig"; sourceTree = "<group>"; };
 		B94C27C06733CF98CE1B2757 /* Pods-AllTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AllTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AllTests/Pods-AllTests.debug.xcconfig"; sourceTree = "<group>"; };
 		CAE086D5B470DA367D415AB0 /* libPods-AllTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AllTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -154,6 +160,8 @@
 		E1486220285AF123EB124008 /* Pods-InteropTestsLocalCleartext.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.debug.xcconfig"; sourceTree = "<group>"; };
 		E4275A759BDBDF143B9B438F /* Pods-InteropTestsRemote.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemote.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemote/Pods-InteropTestsRemote.release.xcconfig"; sourceTree = "<group>"; };
 		E6733B838B28453434B556E2 /* Pods-Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.release.xcconfig"; sourceTree = "<group>"; };
+		E7E4D3FD76E3B745D992AF5F /* Pods-AllTests.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AllTests.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-AllTests/Pods-AllTests.cronet.xcconfig"; sourceTree = "<group>"; };
+		F671D4CAD2864FB203B920B4 /* Pods-Tests.cronet.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.cronet.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.cronet.xcconfig"; sourceTree = "<group>"; };
 		FBD98AC417B9882D32B19F28 /* libPods-CoreCronetEnd2EndTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CoreCronetEnd2EndTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		FD346DB2C23F676C4842F3FF /* libPods-InteropTestsLocalCleartext.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-InteropTestsLocalCleartext.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
@@ -271,6 +279,14 @@
 				4AD97096D13D7416DC91A72A /* Pods-CoreCronetEnd2EndTests.release.xcconfig */,
 				17F60BF2871F6AF85FB3FA12 /* Pods-InteropTestsRemoteWithCronet.debug.xcconfig */,
 				AC414EF7A6BF76ED02B6E480 /* Pods-InteropTestsRemoteWithCronet.release.xcconfig */,
+				E7E4D3FD76E3B745D992AF5F /* Pods-AllTests.cronet.xcconfig */,
+				573450F334B331D0BED8B961 /* Pods-CoreCronetEnd2EndTests.cronet.xcconfig */,
+				AA7CB64B4DD9915AE7C03163 /* Pods-InteropTestsLocalCleartext.cronet.xcconfig */,
+				14B09A58FEE53A7A6B838920 /* Pods-InteropTestsLocalSSL.cronet.xcconfig */,
+				4ADEA1C8BBE10D90940AC68E /* Pods-InteropTestsRemote.cronet.xcconfig */,
+				3F27B2E744482771EB93C394 /* Pods-InteropTestsRemoteWithCronet.cronet.xcconfig */,
+				79C68EFFCB5533475D810B79 /* Pods-RxLibraryUnitTests.cronet.xcconfig */,
+				F671D4CAD2864FB203B920B4 /* Pods-Tests.cronet.xcconfig */,
 			);
 			name = Pods;
 			sourceTree = "<group>";
@@ -1122,6 +1138,173 @@
 			};
 			name = Release;
 		};
+		5EC3C7A01D4FC18C000330E2 /* Cronet */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.3;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+			};
+			name = Cronet;
+		};
+		5EC3C7A11D4FC18C000330E2 /* Cronet */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = F671D4CAD2864FB203B920B4 /* Pods-Tests.cronet.xcconfig */;
+			buildSettings = {
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+			};
+			name = Cronet;
+		};
+		5EC3C7A21D4FC18C000330E2 /* Cronet */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = E7E4D3FD76E3B745D992AF5F /* Pods-AllTests.cronet.xcconfig */;
+			buildSettings = {
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(SDKROOT)/Developer/Library/Frameworks",
+					"$(inherited)",
+				);
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				INFOPLIST_FILE = Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Cronet;
+		};
+		5EC3C7A31D4FC18C000330E2 /* Cronet */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 79C68EFFCB5533475D810B79 /* Pods-RxLibraryUnitTests.cronet.xcconfig */;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.RxLibraryUnitTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Cronet;
+		};
+		5EC3C7A41D4FC18C000330E2 /* Cronet */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 4ADEA1C8BBE10D90940AC68E /* Pods-InteropTestsRemote.cronet.xcconfig */;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Cronet;
+		};
+		5EC3C7A51D4FC18C000330E2 /* Cronet */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 14B09A58FEE53A7A6B838920 /* Pods-InteropTestsLocalSSL.cronet.xcconfig */;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalSSL;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Cronet;
+		};
+		5EC3C7A61D4FC18C000330E2 /* Cronet */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = AA7CB64B4DD9915AE7C03163 /* Pods-InteropTestsLocalCleartext.cronet.xcconfig */;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalCleartext;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Cronet;
+		};
+		5EC3C7A71D4FC18C000330E2 /* Cronet */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 573450F334B331D0BED8B961 /* Pods-CoreCronetEnd2EndTests.cronet.xcconfig */;
+			buildSettings = {
+				CLANG_ANALYZER_NONNULL = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = CoreCronetEnd2EndTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.CoreCronetEnd2EndTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = "$(inherited) \"${PODS_ROOT}/../../../..\"";
+			};
+			name = Cronet;
+		};
+		5EC3C7A81D4FC18C000330E2 /* Cronet */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 3F27B2E744482771EB93C394 /* Pods-InteropTestsRemoteWithCronet.cronet.xcconfig */;
+			buildSettings = {
+				CLANG_ANALYZER_NONNULL = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"$(inherited)",
+					"COCOAPODS=1",
+					"$(inherited)",
+					"GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1",
+					"GRPC_COMPILE_WITH_CRONET=1",
+				);
+				INFOPLIST_FILE = InteropTestsRemoteWithCronet/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsRemoteWithCronet;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Cronet;
+		};
 		5EE84BF91D4717E40050C6CC /* Debug */ = {
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = 17F60BF2871F6AF85FB3FA12 /* Pods-InteropTestsRemoteWithCronet.debug.xcconfig */;
@@ -1135,7 +1318,6 @@
 					"COCOAPODS=1",
 					"$(inherited)",
 					"GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1",
-					"GRPC_COMPILE_WITH_CRONET=1",
 				);
 				INFOPLIST_FILE = InteropTestsRemoteWithCronet/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
@@ -1409,6 +1591,7 @@
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				5E8A5DAC1D3840B4000F8BC4 /* Debug */,
+				5EC3C7A71D4FC18C000330E2 /* Cronet */,
 				5E8A5DAD1D3840B4000F8BC4 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
@@ -1418,6 +1601,7 @@
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				5EE84BF91D4717E40050C6CC /* Debug */,
+				5EC3C7A81D4FC18C000330E2 /* Cronet */,
 				5EE84BFA1D4717E40050C6CC /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
@@ -1427,6 +1611,7 @@
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				63423F4E1B150A5F006CF63C /* Debug */,
+				5EC3C7A21D4FC18C000330E2 /* Cronet */,
 				63423F4F1B150A5F006CF63C /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
@@ -1436,6 +1621,7 @@
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				635697D91B14FC11007A7283 /* Debug */,
+				5EC3C7A01D4FC18C000330E2 /* Cronet */,
 				635697DA1B14FC11007A7283 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
@@ -1445,6 +1631,7 @@
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				635697DC1B14FC11007A7283 /* Debug */,
+				5EC3C7A11D4FC18C000330E2 /* Cronet */,
 				635697DD1B14FC11007A7283 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
@@ -1454,6 +1641,7 @@
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				63DC841C1BE15179000708E8 /* Debug */,
+				5EC3C7A31D4FC18C000330E2 /* Cronet */,
 				63DC841D1BE15179000708E8 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
@@ -1463,6 +1651,7 @@
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				63DC842C1BE15267000708E8 /* Debug */,
+				5EC3C7A41D4FC18C000330E2 /* Cronet */,
 				63DC842D1BE15267000708E8 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
@@ -1472,6 +1661,7 @@
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				63DC843D1BE15294000708E8 /* Debug */,
+				5EC3C7A51D4FC18C000330E2 /* Cronet */,
 				63DC843E1BE15294000708E8 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
@@ -1481,6 +1671,7 @@
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				63DC844C1BE152B5000708E8 /* Debug */,
+				5EC3C7A61D4FC18C000330E2 /* Cronet */,
 				63DC844D1BE152B5000708E8 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme
index e6a052a..d1d616c 100644
--- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme
+++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme
@@ -39,12 +39,6 @@
             </BuildableReference>
             <SkippedTests>
                <Test
-                  Identifier = "GRPCClientTests/testConnectionToRemoteServer">
-               </Test>
-               <Test
-                  Identifier = "GRPCClientTests/testMetadata">
-               </Test>
-               <Test
                   Identifier = "InteropTests">
                </Test>
                <Test
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemoteWithCronet.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemoteWithCronet.xcscheme
index 6d92be8..1d21111 100644
--- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemoteWithCronet.xcscheme
+++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemoteWithCronet.xcscheme
@@ -23,7 +23,7 @@
       </BuildActionEntries>
    </BuildAction>
    <TestAction
-      buildConfiguration = "Debug"
+      buildConfiguration = "Cronet"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       shouldUseLaunchSchemeArgsEnv = "YES">
@@ -57,7 +57,7 @@
       </AdditionalOptions>
    </TestAction>
    <LaunchAction
-      buildConfiguration = "Debug"
+      buildConfiguration = "Cronet"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       launchStyle = "0"
@@ -79,7 +79,7 @@
       </AdditionalOptions>
    </LaunchAction>
    <ProfileAction
-      buildConfiguration = "Release"
+      buildConfiguration = "Cronet"
       shouldUseLaunchSchemeArgsEnv = "YES"
       savedToolIdentifier = ""
       useCustomWorkingDirectory = "NO"
@@ -95,10 +95,10 @@
       </MacroExpansion>
    </ProfileAction>
    <AnalyzeAction
-      buildConfiguration = "Debug">
+      buildConfiguration = "Cronet">
    </AnalyzeAction>
    <ArchiveAction
-      buildConfiguration = "Release"
+      buildConfiguration = "Cronet"
       revealArchiveInOrganizer = "YES">
    </ArchiveAction>
 </Scheme>
diff --git a/src/objective-c/tests/run_tests.sh b/src/objective-c/tests/run_tests.sh
index a265149..81c2885 100755
--- a/src/objective-c/tests/run_tests.sh
+++ b/src/objective-c/tests/run_tests.sh
@@ -44,8 +44,8 @@
              "interop_server before calling this script."
     exit 1
 }
-$BINDIR/interop_server --port=5050 &
-$BINDIR/interop_server --port=5051 --use_tls &
+$BINDIR/interop_server --port=5050 --max_send_message_size=8388608 &
+$BINDIR/interop_server --port=5051 --max_send_message_size=8388608 --use_tls &
 # Kill them when this script exits.
 trap 'kill -9 `jobs -p`' EXIT
 
diff --git a/src/php/.gitignore b/src/php/.gitignore
index ecde2ca..f124cbc 100644
--- a/src/php/.gitignore
+++ b/src/php/.gitignore
@@ -20,3 +20,6 @@
 ext/grpc/ltmain.sh
 composer.lock
 vendor/
+
+*.pb.php
+*_grpc_pb.php
diff --git a/src/php/README.md b/src/php/README.md
index 7e9819b..0e5b4de 100644
--- a/src/php/README.md
+++ b/src/php/README.md
@@ -1,7 +1,8 @@
 
 #Overview
 
-This directory contains source code for PHP implementation of gRPC layered on shared C library.
+This directory contains source code for PHP implementation of gRPC layered on
+shared C library.
 
 #Status
 
@@ -9,17 +10,29 @@
 
 ## Environment
 
-Prerequisite:
+**Prerequisite:**
 * `php` 5.5 or above, 7.0 or above
-* `pear` and `pecl`
-* `phpunit`
+* `pecl`
+* `composer`
+* `phpunit` (optional)
 
-**PEAR:**
+**Ubuntu/Debian:**
+```sh
+$ sudo apt-get install php5 php5-dev
+```
+
+**PEAR/PECL:**
 ```sh
 $ curl -O http://pear.php.net/go-pear.phar
 $ sudo php -d detect_unicode=0 go-pear.phar
 ```
 
+**Composer:**
+```sh
+$ curl -sS https://getcomposer.org/installer | php
+$ sudo mv composer.phar /usr/local/bin/composer
+```
+
 **PHPUnit:**
 ```sh
 $ wget https://phar.phpunit.de/phpunit-old.phar
@@ -29,15 +42,31 @@
 
 ## Quick Install
 
-Install the gRPC PHP extension
+**Install the gRPC PHP extension**
 
 ```sh
 sudo pecl install grpc
 ```
 
-This will compile and install the gRPC PHP extension into the standard PHP extension directory. You should be able to run the [unit tests](#unit-tests), with the PHP extension installed.
+This will compile and install the gRPC PHP extension into the standard PHP
+extension directory. You should be able to run the [unit tests](#unit-tests),
+with the PHP extension installed.
 
-To run tests with generated stub code from `.proto` files, you will also need the `composer`, `protoc` and `protoc-gen-php` binaries. You can find out how to get these [below](#generated-code-tests).
+
+**Add the gRPC PHP library as a Composer dependency**
+
+You need to add this to your project's `composer.json` file.
+
+```
+  "require": {
+    "grpc/grpc": "v1.0.0"
+  }
+```
+
+To run tests with generated stub code from `.proto` files, you will also need
+the `composer` and `protoc` binaries. You can find out how to get these
+[below](#generated-code-tests).
+
 
 ## Build from Source
 
@@ -98,45 +127,46 @@
 
 ## Generated Code Tests
 
-This section specifies the prerequisites for running the generated code tests, as well as how to run the tests themselves.
+This section specifies the prerequisites for running the generated code tests,
+as well as how to run the tests themselves.
 
 ### Composer
 
-If you don't have it already, install `composer` to pull in some runtime dependencies based on the `composer.json` file.
+Install the runtime dependencies via `composer install`.
 
 ```sh
-$ curl -sS https://getcomposer.org/installer | php
-$ sudo mv composer.phar /usr/local/bin/composer
-
 $ cd grpc/src/php
 $ composer install
 ```
 
 ### Protobuf compiler
 
-Again if you don't have it already, you need to install the protobuf compiler `protoc`, version 3.0.0+.
+Again if you don't have it already, you need to install the protobuf compiler
+`protoc`, version 3.1.0+.
 
-If you compiled the gRPC C core library from source above, the `protoc` binary should have been installed as well. If it hasn't been installed, you can run the following commands to install it.
+If `protoc` hasn't been installed, you can download the `protoc` binaries from
+[the protocol buffers Github repository](https://github.com/google/protobuf/releases).
+
+If you really must compile `protoc` from source, you can run the following
+commands, but this is risky because there is no easy way to uninstall /
+upgrade to a newer release.
 
 ```sh
 $ cd grpc/third_party/protobuf
-$ sudo make install   # 'make' should have been run by core grpc
+$ ./autogen.sh && ./configure && make
+$ sudo make install
 ```
 
-Alternatively, you can download `protoc` binaries from [the protocol buffers Github repository](https://github.com/google/protobuf/releases).
 
+### PHP Protoc Plugin
 
-### PHP protobuf compiler
+You need the gRPC PHP protoc plugin to generate the client stub classes.
 
-You need to install `protoc-gen-php` to generate stub class `.php` files from service definition `.proto` files.
+It should already been compiled when you run `make` from the root directory
+of this repo. The plugin can be found in the `bins/opt` directory. We are
+planning to provide a better way to download and install the plugin
+in the future.
 
-```sh
-$ git clone https://github.com/stanley-cheung/Protobuf-PHP
-$ cd Protobuf-PHP
-$ gem install rake ronn
-$ rake pear:package version=1.0
-$ sudo pear install Protobuf-1.0.tgz
-```
 
 ### Client Stub
 
@@ -149,7 +179,8 @@
 
 ### Run test server
 
-Run a local server serving the math services. Please see [Node][] for how to run an example server.
+Run a local server serving the math services. Please see [Node][] for how to
+run an example server.
 
 ```sh
 $ cd grpc
diff --git a/src/php/bin/determine_extension_dir.sh b/src/php/bin/determine_extension_dir.sh
index a598825..e3d6bbc 100755
--- a/src/php/bin/determine_extension_dir.sh
+++ b/src/php/bin/determine_extension_dir.sh
@@ -32,7 +32,7 @@
 if [ ! -e $default_extension_dir/grpc.so ]; then
   # the grpc extension is not found in the default PHP extension dir
   # try the source modules directory
-  module_dir=../ext/grpc/modules
+  module_dir=$(pwd)/../ext/grpc/modules
   if [ ! -e $module_dir/grpc.so ]; then
     echo "Please run 'phpize && ./configure && make' from ext/grpc first"
     exit 1
diff --git a/src/php/bin/generate_proto_php.sh b/src/php/bin/generate_proto_php.sh
index 16f9374..c558bc5 100755
--- a/src/php/bin/generate_proto_php.sh
+++ b/src/php/bin/generate_proto_php.sh
@@ -28,13 +28,32 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-
 set +e
-cd $(dirname $0)
+cd $(dirname $0)/../../..
 
-gen_code='../tests/generated_code'
-interop='../tests/interop'
+protoc --proto_path=src/proto/math \
+       --php_out=src/php/tests/generated_code \
+       --grpc_out=src/php/tests/generated_code \
+       --plugin=protoc-gen-grpc=bins/opt/grpc_php_plugin \
+       src/proto/math/math.proto
 
-protoc-gen-php -i $gen_code -o $gen_code $gen_code/math.proto
+# replace the Empty message with EmptyMessage
+# because Empty is a PHP reserved word
+sed -i 's/message Empty/message EmptyMessage/g' \
+    src/proto/grpc/testing/empty.proto
+sed -i 's/grpc\.testing\.Empty/grpc\.testing\.EmptyMessage/g' \
+    src/proto/grpc/testing/test.proto
 
-protoc-gen-php -i $interop -o $interop $interop/test.proto
+protoc --proto_path=. \
+       --php_out=src/php/tests/interop \
+       --grpc_out=src/php/tests/interop \
+       --plugin=protoc-gen-grpc=bins/opt/grpc_php_plugin \
+       src/proto/grpc/testing/messages.proto \
+       src/proto/grpc/testing/empty.proto \
+       src/proto/grpc/testing/test.proto
+
+# change it back
+sed -i 's/message EmptyMessage/message Empty/g' \
+    src/proto/grpc/testing/empty.proto
+sed -i 's/grpc\.testing\.EmptyMessage/grpc\.testing\.Empty/g' \
+    src/proto/grpc/testing/test.proto
diff --git a/src/php/bin/interop_client.sh b/src/php/bin/interop_client.sh
index 2af49cd..7fa4686 100755
--- a/src/php/bin/interop_client.sh
+++ b/src/php/bin/interop_client.sh
@@ -31,5 +31,6 @@
 set -e
 cd $(dirname $0)
 source ./determine_extension_dir.sh
+cd ../tests/interop
 php $extension_dir -d max_execution_time=300 \
-  ../tests/interop/interop_client.php $@ 1>&2
+  interop_client.php $@ 1>&2
diff --git a/src/php/composer.json b/src/php/composer.json
index 1eacc64..6042094 100644
--- a/src/php/composer.json
+++ b/src/php/composer.json
@@ -5,10 +5,10 @@
   "keywords": ["rpc"],
   "homepage": "http://grpc.io",
   "license": "BSD-3-Clause",
-  "version": "1.0.0",
+  "version": "1.1.0",
   "require": {
     "php": ">=5.5.0",
-    "stanley-cheung/protobuf-php": "v0.6"
+    "google/protobuf": "v3.1.0-alpha-1"
   },
   "require-dev": {
     "google/auth": "v0.9"
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
index 66ca151..3a49ea8 100644
--- a/src/php/ext/grpc/call.c
+++ b/src/php/ext/grpc/call.c
@@ -69,8 +69,8 @@
   }
 PHP_GRPC_FREE_WRAPPED_FUNC_END()
 
-/* Initializes an instance of wrapped_grpc_call to be associated with an object
- * of a class specified by class_type */
+/* Initializes an instance of wrapped_grpc_call to be associated with an
+ * object of a class specified by class_type */
 php_grpc_zend_object create_wrapped_grpc_call(zend_class_entry *class_type
                                               TSRMLS_DC) {
   PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_call);
@@ -164,6 +164,9 @@
     if (key_type1 != HASH_KEY_IS_STRING) {
       return false;
     }
+    if (!grpc_header_key_is_legal(key1, strlen(key1))) {
+      return false;
+    }
     inner_array_hash = Z_ARRVAL_P(inner_array);
     PHP_GRPC_HASH_FOREACH_VAL_START(inner_array_hash, value)
       if (Z_TYPE_P(value) != IS_STRING) {
@@ -192,10 +195,11 @@
 
 /**
  * Constructs a new instance of the Call class.
- * @param Channel $channel The channel to associate the call with. Must not be
- *     closed.
+ * @param Channel $channel_obj The channel to associate the call with.
+ *                             Must not be closed.
  * @param string $method The method to call
- * @param Timeval $absolute_deadline The deadline for completing the call
+ * @param Timeval $deadline_obj The deadline for completing the call
+ * @param string $host_override The host is set by user (optional)
  */
 PHP_METHOD(Call, __construct) {
   zval *channel_obj;
@@ -234,7 +238,7 @@
 
 /**
  * Start a batch of RPC actions.
- * @param array batch Array of actions to take
+ * @param array $array Array of actions to take
  * @return object Object with results of all actions
  */
 PHP_METHOD(Call, startBatch) {
@@ -512,8 +516,9 @@
 }
 
 /**
- * Cancel the call. This will cause the call to end with STATUS_CANCELLED if it
- * has not already ended with another status.
+ * Cancel the call. This will cause the call to end with STATUS_CANCELLED
+ * if it has not already ended with another status.
+ * @return void
  */
 PHP_METHOD(Call, cancel) {
   wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis());
@@ -522,8 +527,8 @@
 
 /**
  * Set the CallCredentials for this call.
- * @param CallCredentials creds_obj The CallCredentials object
- * @param int The error code
+ * @param CallCredentials $creds_obj The CallCredentials object
+ * @return int The error code
  */
 PHP_METHOD(Call, setCredentials) {
   zval *creds_obj;
diff --git a/src/php/ext/grpc/call_credentials.c b/src/php/ext/grpc/call_credentials.c
index 6921a5d..3aafc3a 100644
--- a/src/php/ext/grpc/call_credentials.c
+++ b/src/php/ext/grpc/call_credentials.c
@@ -87,8 +87,8 @@
 
 /**
  * Create composite credentials from two existing credentials.
- * @param CallCredentials cred1 The first credential
- * @param CallCredentials cred2 The second credential
+ * @param CallCredentials $cred1_obj The first credential
+ * @param CallCredentials $cred2_obj The second credential
  * @return CallCredentials The new composite credentials object
  */
 PHP_METHOD(CallCredentials, createComposite) {
@@ -119,7 +119,7 @@
 
 /**
  * Create a call credentials object from the plugin API
- * @param function callback The callback function
+ * @param function $fci The callback function
  * @return CallCredentials The new call credentials object
  */
 PHP_METHOD(CallCredentials, createFromPlugin) {
@@ -192,23 +192,15 @@
   /* call the user callback function */
   zend_call_function(state->fci, state->fci_cache TSRMLS_CC);
 
-  if (Z_TYPE_P(retval) != IS_ARRAY) {
-    zend_throw_exception(spl_ce_InvalidArgumentException,
-                         "plugin callback must return metadata array",
-                         1 TSRMLS_CC);
-    return;
-  }
-
-  grpc_metadata_array metadata;
-  if (!create_metadata_array(retval, &metadata)) {
-    zend_throw_exception(spl_ce_InvalidArgumentException,
-                         "invalid metadata", 1 TSRMLS_CC);
-    grpc_metadata_array_destroy(&metadata);
-    return;
-  }
-
-  /* TODO: handle error */
   grpc_status_code code = GRPC_STATUS_OK;
+  grpc_metadata_array metadata;
+
+  if (Z_TYPE_P(retval) != IS_ARRAY) {
+    code = GRPC_STATUS_INVALID_ARGUMENT;
+  } else if (!create_metadata_array(retval, &metadata)) {
+    grpc_metadata_array_destroy(&metadata);
+    code = GRPC_STATUS_INVALID_ARGUMENT;
+  }
 
   /* Pass control back to core */
   cb(user_data, metadata.metadata, metadata.count, code, NULL);
diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c
index b5a2c9f..4ce4f30 100644
--- a/src/php/ext/grpc/channel.c
+++ b/src/php/ext/grpc/channel.c
@@ -125,7 +125,7 @@
  * "credentials" key mapping to a ChannelCredentials object, a secure channel
  * will be created with those credentials.
  * @param string $target The hostname to associate with this channel
- * @param array $args The arguments to pass to the Channel (optional)
+ * @param array $args_array The arguments to pass to the Channel
  */
 PHP_METHOD(Channel, __construct) {
   wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
@@ -182,7 +182,7 @@
 
 /**
  * Get the connectivity state of the channel
- * @param bool (optional) try to connect on the channel
+ * @param bool $try_to_connect Try to connect on the channel (optional)
  * @return long The grpc connectivity state
  */
 PHP_METHOD(Channel, getConnectivityState) {
@@ -202,8 +202,8 @@
 
 /**
  * Watch the connectivity state of the channel until it changed
- * @param long The previous connectivity state of the channel
- * @param Timeval The deadline this function should wait until
+ * @param long $last_state The previous connectivity state of the channel
+ * @param Timeval $deadline_obj The deadline this function should wait until
  * @return bool If the connectivity state changes from last_state
  *              before deadline
  */
@@ -233,6 +233,7 @@
 
 /**
  * Close the channel
+ * @return void
  */
 PHP_METHOD(Channel, close) {
   wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis());
diff --git a/src/php/ext/grpc/channel_credentials.c b/src/php/ext/grpc/channel_credentials.c
index 0b356aa..a32c4a4 100644
--- a/src/php/ext/grpc/channel_credentials.c
+++ b/src/php/ext/grpc/channel_credentials.c
@@ -97,7 +97,7 @@
 
 /**
  * Set default roots pem.
- * @param string pem_roots PEM encoding of the server root certificates
+ * @param string $pem_roots PEM encoding of the server root certificates
  * @return void
  */
 PHP_METHOD(ChannelCredentials, setDefaultRootsPem) {
@@ -129,11 +129,11 @@
 
 /**
  * Create SSL credentials.
- * @param string pem_root_certs PEM encoding of the server root certificates
- * @param string pem_private_key PEM encoding of the client's private key
- *     (optional)
- * @param string pem_cert_chain PEM encoding of the client's certificate chain
- *     (optional)
+ * @param string $pem_root_certs PEM encoding of the server root certificates
+ * @param string $pem_key_cert_pair.private_key PEM encoding of the client's
+ *                                              private key (optional)
+ * @param string $pem_key_cert_pair.cert_chain PEM encoding of the client's
+ *                                             certificate chain (optional)
  * @return ChannelCredentials The new SSL credentials object
  */
 PHP_METHOD(ChannelCredentials, createSsl) {
@@ -168,8 +168,8 @@
 
 /**
  * Create composite credentials from two existing credentials.
- * @param ChannelCredentials cred1 The first credential
- * @param CallCredentials cred2 The second credential
+ * @param ChannelCredentials $cred1_obj The first credential
+ * @param CallCredentials $cred2_obj The second credential
  * @return ChannelCredentials The new composite credentials object
  */
 PHP_METHOD(ChannelCredentials, createComposite) {
diff --git a/src/php/ext/grpc/php7_wrapper.h b/src/php/ext/grpc/php7_wrapper.h
index fd8d356..1d78241 100644
--- a/src/php/ext/grpc/php7_wrapper.h
+++ b/src/php/ext/grpc/php7_wrapper.h
@@ -143,8 +143,7 @@
 
 #define PHP_GRPC_RETURN_STRING(val, dup) RETURN_STRING(val)
 #define PHP_GRPC_MAKE_STD_ZVAL(pzv) \
-  zval _stack_zval_##pzv; \
-  pzv = &(_stack_zval_##pzv)
+  pzv = (zval *)emalloc(sizeof(zval));
 #define PHP_GRPC_DELREF(zv)
 
 #define PHP_GRPC_WRAP_OBJECT_START(name) \
diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c
index fc20c42..2217a4f 100644
--- a/src/php/ext/grpc/server.c
+++ b/src/php/ext/grpc/server.c
@@ -72,8 +72,8 @@
   }
 PHP_GRPC_FREE_WRAPPED_FUNC_END()
 
-/* Initializes an instance of wrapped_grpc_call to be associated with an object
- * of a class specified by class_type */
+/* Initializes an instance of wrapped_grpc_call to be associated with an
+ * object of a class specified by class_type */
 php_grpc_zend_object create_wrapped_grpc_server(zend_class_entry *class_type
                                                 TSRMLS_DC) {
   PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_server);
@@ -84,7 +84,7 @@
 
 /**
  * Constructs a new instance of the Server class
- * @param array $args The arguments to pass to the server (optional)
+ * @param array $args_array The arguments to pass to the server (optional)
  */
 PHP_METHOD(Server, __construct) {
   wrapped_grpc_server *server = Z_WRAPPED_GRPC_SERVER_P(getThis());
@@ -116,7 +116,7 @@
  * Request a call on a server. Creates a single GRPC_SERVER_RPC_NEW event.
  * @param long $tag_new The tag to associate with the new request
  * @param long $tag_cancel The tag to use if the call is cancelled
- * @return Void
+ * @return void
  */
 PHP_METHOD(Server, requestCall) {
   grpc_call_error error_code;
@@ -180,7 +180,7 @@
 /**
  * Add a http2 over tcp listener.
  * @param string $addr The address to add
- * @return true on success, false on failure
+ * @return bool True on success, false on failure
  */
 PHP_METHOD(Server, addHttp2Port) {
   const char *addr;
@@ -197,6 +197,12 @@
   RETURN_LONG(grpc_server_add_insecure_http2_port(server->wrapped, addr));
 }
 
+/**
+ * Add a secure http2 over tcp listener.
+ * @param string $addr The address to add
+ * @param ServerCredentials The ServerCredentials object
+ * @return bool True on success, false on failure
+ */
 PHP_METHOD(Server, addSecureHttp2Port) {
   const char *addr;
   php_grpc_int addr_len;
@@ -220,7 +226,7 @@
 
 /**
  * Start a server - tells all listeners to start listening
- * @return Void
+ * @return void
  */
 PHP_METHOD(Server, start) {
   wrapped_grpc_server *server = Z_WRAPPED_GRPC_SERVER_P(getThis());
diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c
index b05896a..1610c8e 100644
--- a/src/php/ext/grpc/server_credentials.c
+++ b/src/php/ext/grpc/server_credentials.c
@@ -61,8 +61,8 @@
   }
 PHP_GRPC_FREE_WRAPPED_FUNC_END()
 
-/* Initializes an instace of wrapped_grpc_server_credentials to be associated
- * with an object of a class specified by class_type */
+/* Initializes an instace of wrapped_grpc_server_credentials to be
+ * associated with an object of a class specified by class_type */
 php_grpc_zend_object create_wrapped_grpc_server_credentials(
     zend_class_entry *class_type TSRMLS_DC) {
   PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_server_credentials);
diff --git a/src/php/ext/grpc/timeval.c b/src/php/ext/grpc/timeval.c
index e145d96..945231b 100644
--- a/src/php/ext/grpc/timeval.c
+++ b/src/php/ext/grpc/timeval.c
@@ -80,7 +80,7 @@
 
 /**
  * Constructs a new instance of the Timeval class
- * @param long $usec The number of microseconds in the interval
+ * @param long $microseconds The number of microseconds in the interval
  */
 PHP_METHOD(Timeval, __construct) {
   wrapped_grpc_timeval *timeval = Z_WRAPPED_GRPC_TIMEVAL_P(getThis());
@@ -100,7 +100,7 @@
 /**
  * Adds another Timeval to this one and returns the sum. Calculations saturate
  * at infinities.
- * @param Timeval $other The other Timeval object to add
+ * @param Timeval $other_obj The other Timeval object to add
  * @return Timeval A new Timeval object containing the sum
  */
 PHP_METHOD(Timeval, add) {
@@ -126,8 +126,8 @@
 /**
  * Subtracts another Timeval from this one and returns the difference.
  * Calculations saturate at infinities.
- * @param Timeval $other The other Timeval object to subtract
- * @param Timeval A new Timeval object containing the sum
+ * @param Timeval $other_obj The other Timeval object to subtract
+ * @return Timeval A new Timeval object containing the diff 
  */
 PHP_METHOD(Timeval, subtract) {
   zval *other_obj;
@@ -150,10 +150,10 @@
 }
 
 /**
- * Return negative, 0, or positive according to whether a < b, a == b, or a > b
- * respectively.
- * @param Timeval $a The first time to compare
- * @param Timeval $b The second time to compare
+ * Return negative, 0, or positive according to whether a < b, a == b,
+ * or a > b respectively.
+ * @param Timeval $a_obj The first time to compare
+ * @param Timeval $b_obj The second time to compare
  * @return long
  */
 PHP_METHOD(Timeval, compare) {
@@ -176,9 +176,9 @@
 
 /**
  * Checks whether the two times are within $threshold of each other
- * @param Timeval $a The first time to compare
- * @param Timeval $b The second time to compare
- * @param Timeval $threshold The threshold to check against
+ * @param Timeval $a_obj The first time to compare
+ * @param Timeval $b_obj The second time to compare
+ * @param Timeval $thresh_obj The threshold to check against
  * @return bool True if $a and $b are within $threshold, False otherwise
  */
 PHP_METHOD(Timeval, similar) {
diff --git a/src/php/lib/Grpc/AbstractCall.php b/src/php/lib/Grpc/AbstractCall.php
index c86d298..e24be3f 100644
--- a/src/php/lib/Grpc/AbstractCall.php
+++ b/src/php/lib/Grpc/AbstractCall.php
@@ -34,8 +34,15 @@
 
 namespace Grpc;
 
+/**
+ * Class AbstractCall
+ * @package Grpc
+ */
 abstract class AbstractCall
 {
+    /**
+     * @var Call
+     */
     protected $call;
     protected $deserialize;
     protected $metadata;
@@ -51,13 +58,15 @@
      *                              the response
      * @param array    $options     Call options (optional)
      */
-    public function __construct(Channel $channel,
-                                $method,
-                                $deserialize,
-                                $options = [])
-    {
-        if (isset($options['timeout']) &&
-            is_numeric($timeout = $options['timeout'])) {
+    public function __construct(
+        Channel $channel,
+        $method,
+        $deserialize,
+        $options = []
+    ) {
+        if (array_key_exists('timeout', $options) &&
+            is_numeric($timeout = $options['timeout'])
+        ) {
             $now = Timeval::now();
             $delta = new Timeval($timeout);
             $deadline = $now->add($delta);
@@ -68,17 +77,19 @@
         $this->deserialize = $deserialize;
         $this->metadata = null;
         $this->trailing_metadata = null;
-        if (isset($options['call_credentials_callback']) &&
+        if (array_key_exists('call_credentials_callback', $options) &&
             is_callable($call_credentials_callback =
-                        $options['call_credentials_callback'])) {
+                $options['call_credentials_callback'])
+        ) {
             $call_credentials = CallCredentials::createFromPlugin(
-                $call_credentials_callback);
+                $call_credentials_callback
+            );
             $this->call->setCredentials($call_credentials);
         }
     }
 
     /**
-     * @return The metadata sent by the server.
+     * @return mixed The metadata sent by the server.
      */
     public function getMetadata()
     {
@@ -86,7 +97,7 @@
     }
 
     /**
-     * @return The trailing metadata sent by the server.
+     * @return mixed The trailing metadata sent by the server.
      */
     public function getTrailingMetadata()
     {
@@ -110,11 +121,28 @@
     }
 
     /**
+     * Serialize a message to the protobuf binary format
+     *
+     * @param mixed $data The Protobuf message
+     *
+     * @return string The protobuf binary format
+     */
+    protected function serializeMessage($data) {
+        // Proto3 implementation
+        if (method_exists($data, 'encode')) {
+            return $data->encode();
+        }
+
+        // Protobuf-PHP implementation
+        return $data->serialize();
+    }
+
+    /**
      * Deserialize a response value to an object.
      *
      * @param string $value The binary value to deserialize
      *
-     * @return The deserialized value
+     * @return mixed The deserialized value
      */
     protected function deserializeResponse($value)
     {
@@ -122,6 +150,15 @@
             return;
         }
 
+        // Proto3 implementation
+        if (is_array($this->deserialize)) {
+            list($className, $deserializeFunc) = $this->deserialize;
+            $obj = new $className();
+            $obj->$deserializeFunc($value);
+            return $obj;
+        }
+
+        // Protobuf-PHP implementation
         return call_user_func($this->deserialize, $value);
     }
 
diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php
index 2fec1bd..8a7f657 100644
--- a/src/php/lib/Grpc/BaseStub.php
+++ b/src/php/lib/Grpc/BaseStub.php
@@ -41,6 +41,7 @@
 class BaseStub
 {
     private $hostname;
+    private $hostname_override;
     private $channel;
 
     // a callback function
@@ -75,6 +76,9 @@
         } else {
             $opts['grpc.primary_user_agent'] = '';
         }
+        if (!empty($opts['grpc.ssl_target_name_override'])) {
+            $this->hostname_override = $opts['grpc.ssl_target_name_override'];
+        }
         $opts['grpc.primary_user_agent'] .=
             'grpc-php/'.$package_config['version'];
         if (!array_key_exists('credentials', $opts)) {
@@ -173,7 +177,13 @@
         }
         $service_name = substr($method, 0, $last_slash_idx);
 
-        return 'https://'.$this->hostname.$service_name;
+        if ($this->hostname_override) {
+            $hostname = $this->hostname_override;
+        } else {
+            $hostname = $this->hostname;
+        }
+
+        return 'https://'.$hostname.$service_name;
     }
 
     /**
diff --git a/src/php/lib/Grpc/BidiStreamingCall.php b/src/php/lib/Grpc/BidiStreamingCall.php
index c2fdb94..f0e1e81 100644
--- a/src/php/lib/Grpc/BidiStreamingCall.php
+++ b/src/php/lib/Grpc/BidiStreamingCall.php
@@ -45,7 +45,7 @@
      *
      * @param array $metadata Metadata to send with the call, if applicable
      */
-    public function start($metadata = [])
+    public function start(array $metadata = [])
     {
         $this->call->startBatch([
             OP_SEND_INITIAL_METADATA => $metadata,
@@ -55,7 +55,7 @@
     /**
      * Reads the next value from the server.
      *
-     * @return The next value from the server, or null if there is none
+     * @return mixed The next value from the server, or null if there is none
      */
     public function read()
     {
@@ -81,8 +81,8 @@
      */
     public function write($data, $options = [])
     {
-        $message_array = ['message' => $data->serialize()];
-        if (isset($options['flags'])) {
+        $message_array = ['message' => $this->serializeMessage($data)];
+        if (array_key_exists('flags', $options)) {
             $message_array['flags'] = $options['flags'];
         }
         $this->call->startBatch([
@@ -103,8 +103,8 @@
     /**
      * Wait for the server to send the status, and return it.
      *
-     * @return object The status object, with integer $code, string $details,
-     *                and array $metadata members
+     * @return \stdClass The status object, with integer $code, string $details,
+     *                   and array $metadata members
      */
     public function getStatus()
     {
diff --git a/src/php/lib/Grpc/ClientStreamingCall.php b/src/php/lib/Grpc/ClientStreamingCall.php
index 4050f7e..20db809 100644
--- a/src/php/lib/Grpc/ClientStreamingCall.php
+++ b/src/php/lib/Grpc/ClientStreamingCall.php
@@ -60,10 +60,10 @@
      * @param array      $options an array of options, possible keys:
      *                            'flags' => a number
      */
-    public function write($data, $options = [])
+    public function write($data, array $options = [])
     {
-        $message_array = ['message' => $data->serialize()];
-        if (isset($options['flags'])) {
+        $message_array = ['message' => $this->serializeMessage($data)];
+        if (array_key_exists('flags', $options)) {
             $message_array['flags'] = $options['flags'];
         }
         $this->call->startBatch([
@@ -74,7 +74,7 @@
     /**
      * Wait for the server to respond with data and a status.
      *
-     * @return [response data, status]
+     * @return array [response data, status]
      */
     public function wait()
     {
diff --git a/src/php/lib/Grpc/ServerStreamingCall.php b/src/php/lib/Grpc/ServerStreamingCall.php
index ba89d9f..5aeeafa 100644
--- a/src/php/lib/Grpc/ServerStreamingCall.php
+++ b/src/php/lib/Grpc/ServerStreamingCall.php
@@ -36,22 +36,22 @@
 
 /**
  * Represents an active call that sends a single message and then gets a stream
- * of reponses.
+ * of responses.
  */
 class ServerStreamingCall extends AbstractCall
 {
     /**
      * Start the call.
      *
-     * @param $data The data to send
+     * @param mixed $data     The data to send
      * @param array $metadata Metadata to send with the call, if applicable
      * @param array $options  an array of options, possible keys:
      *                        'flags' => a number
      */
     public function start($data, $metadata = [], $options = [])
     {
-        $message_array = ['message' => $data->serialize()];
-        if (isset($options['flags'])) {
+        $message_array = ['message' => $this->serializeMessage($data)];
+        if (array_key_exists('flags', $options)) {
             $message_array['flags'] = $options['flags'];
         }
         $event = $this->call->startBatch([
@@ -64,7 +64,7 @@
     }
 
     /**
-     * @return An iterator of response values
+     * @return mixed An iterator of response values
      */
     public function responses()
     {
@@ -82,8 +82,8 @@
     /**
      * Wait for the server to send the status, and return it.
      *
-     * @return object The status object, with integer $code, string $details,
-     *                and array $metadata members
+     * @return \stdClass The status object, with integer $code, string $details,
+     *                   and array $metadata members
      */
     public function getStatus()
     {
diff --git a/src/php/lib/Grpc/UnaryCall.php b/src/php/lib/Grpc/UnaryCall.php
index a71b05d..e8eb648 100644
--- a/src/php/lib/Grpc/UnaryCall.php
+++ b/src/php/lib/Grpc/UnaryCall.php
@@ -43,14 +43,14 @@
     /**
      * Start the call.
      *
-     * @param $data The data to send
+     * @param mixed $data     The data to send
      * @param array $metadata Metadata to send with the call, if applicable
      * @param array $options  an array of options, possible keys:
      *                        'flags' => a number
      */
     public function start($data, $metadata = [], $options = [])
     {
-        $message_array = ['message' => $data->serialize()];
+        $message_array = ['message' => $this->serializeMessage($data)];
         if (isset($options['flags'])) {
             $message_array['flags'] = $options['flags'];
         }
@@ -66,7 +66,7 @@
     /**
      * Wait for the server to respond with data and a status.
      *
-     * @return [response data, status]
+     * @return array [response data, status]
      */
     public function wait()
     {
diff --git a/src/php/tests/generated_code/AbstractGeneratedCodeTest.php b/src/php/tests/generated_code/AbstractGeneratedCodeTest.php
index 75922d4..8fe9bc2 100644
--- a/src/php/tests/generated_code/AbstractGeneratedCodeTest.php
+++ b/src/php/tests/generated_code/AbstractGeneratedCodeTest.php
@@ -32,7 +32,8 @@
  *
  */
 require_once realpath(dirname(__FILE__).'/../../vendor/autoload.php');
-require_once dirname(__FILE__).'/math.php';
+require_once dirname(__FILE__).'/math.pb.php';
+require_once dirname(__FILE__).'/math_grpc_pb.php';
 
 abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase
 {
@@ -92,7 +93,7 @@
     public function testTimeout()
     {
         $div_arg = new math\DivArgs();
-        $call = self::$client->Div($div_arg, [], ['timeout' => 100]);
+        $call = self::$client->Div($div_arg, [], ['timeout' => 1]);
         list($response, $status) = $call->wait();
         $this->assertSame(\Grpc\STATUS_DEADLINE_EXCEEDED, $status->code);
     }
diff --git a/src/php/tests/generated_code/math.proto b/src/php/tests/generated_code/math.proto
deleted file mode 100644
index c872ee6..0000000
--- a/src/php/tests/generated_code/math.proto
+++ /dev/null
@@ -1,83 +0,0 @@
-
-// Copyright 2015, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// TODO: start using src/proto/math/math.proto and remove this file once
-// PHP supports proto3.
-
-syntax = "proto2";
-
-package math;
-
-message DivArgs {
-  optional int64 dividend = 1 [default = 0];
-  optional int64 divisor = 2 [default = 0];
-}
-
-message DivReply {
-  optional int64 quotient = 1 [default = 0];
-  optional int64 remainder = 2 [default = 0];
-}
-
-message FibArgs {
-  optional int64 limit = 1 [default = 0];
-}
-
-message Num {
-  optional int64 num = 1 [default = 0];
-}
-
-message FibReply {
-  optional int64 count = 1 [default = 0];
-}
-
-service Math {
-  // Div divides args.dividend by args.divisor and returns the quotient and
-  // remainder.
-  rpc Div (DivArgs) returns (DivReply) {
-  }
-
-  // DivMany accepts an arbitrary number of division args from the client stream
-  // and sends back the results in the reply stream.  The stream continues until
-  // the client closes its end; the server does the same after sending all the
-  // replies.  The stream ends immediately if either end aborts.
-  rpc DivMany (stream DivArgs) returns (stream DivReply) {
-  }
-
-  // Fib generates numbers in the Fibonacci sequence.  If args.limit > 0, Fib
-  // generates up to limit numbers; otherwise it continues until the call is
-  // canceled.  Unlike Fib above, Fib has no final FibReply.
-  rpc Fib (FibArgs) returns (stream Num) {
-  }
-
-  // Sum sums a stream of numbers, returning the final result once the stream
-  // is closed.
-  rpc Sum (stream Num) returns (Num) {
-  }
-}
diff --git a/src/php/tests/interop/empty.proto b/src/php/tests/interop/empty.proto
deleted file mode 100644
index 4200d7b..0000000
--- a/src/php/tests/interop/empty.proto
+++ /dev/null
@@ -1,43 +0,0 @@
-
-// Copyright 2015, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-syntax = "proto2";
-
-package grpc.testing;
-
-// An empty message that you can re-use to avoid defining duplicated empty
-// messages in your project. A typical example is to use it as argument or the
-// return value of a service API. For instance:
-//
-//   service Foo {
-//     rpc Bar (grpc.testing.EmptyMessage) returns (grpc.testing.EmptyMessage) { };
-//   };
-//
-message EmptyMessage {}
diff --git a/src/php/tests/interop/interop_client.php b/src/php/tests/interop/interop_client.php
index bf40549..124d324 100755
--- a/src/php/tests/interop/interop_client.php
+++ b/src/php/tests/interop/interop_client.php
@@ -32,9 +32,8 @@
  *
  */
 require_once realpath(dirname(__FILE__).'/../../vendor/autoload.php');
-require 'empty.php';
-require 'messages.php';
-require 'test.php';
+require 'src/proto/grpc/testing/test.pb.php';
+require 'src/proto/grpc/testing/test_grpc_pb.php';
 use Google\Auth\CredentialsLoader;
 use Google\Auth\ApplicationDefaultCredentials;
 use GuzzleHttp\ClientInterface;
@@ -54,6 +53,15 @@
     }
 }
 
+function hardAssertIfStatusOk($status)
+{
+    if ($status->code !== Grpc\STATUS_OK) {
+        echo "Call did not complete successfully. Status object:\n";
+        var_dump($status);
+        exit(1);
+    }
+}
+
 /**
  * Run the empty_unary test.
  *
@@ -61,8 +69,9 @@
  */
 function emptyUnary($stub)
 {
-    list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait();
-    hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
+    list($result, $status) =
+        $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait();
+    hardAssertIfStatusOk($status);
     hardAssert($result !== null, 'Call completed with a null response');
 }
 
@@ -83,8 +92,8 @@
  * @param $fillUsername boolean whether to fill result with username
  * @param $fillOauthScope boolean whether to fill result with oauth scope
  */
-function performLargeUnary($stub, $fillUsername = false, $fillOauthScope = false,
-                           $callback = false)
+function performLargeUnary($stub, $fillUsername = false,
+                           $fillOauthScope = false, $callback = false)
 {
     $request_len = 271828;
     $response_len = 314159;
@@ -99,21 +108,21 @@
     $request->setFillUsername($fillUsername);
     $request->setFillOauthScope($fillOauthScope);
 
-    $options = false;
+    $options = [];
     if ($callback) {
         $options['call_credentials_callback'] = $callback;
     }
 
     list($result, $status) = $stub->UnaryCall($request, [], $options)->wait();
-    hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
+    hardAssertIfStatusOk($status);
     hardAssert($result !== null, 'Call returned a null response');
     $payload = $result->getPayload();
     hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
-         'Payload had the wrong type');
+               'Payload had the wrong type');
     hardAssert(strlen($payload->getBody()) === $response_len,
-         'Payload had the wrong length');
+               'Payload had the wrong length');
     hardAssert($payload->getBody() === str_repeat("\0", $response_len),
-         'Payload had the wrong content');
+               'Payload had the wrong content');
 
     return $result;
 }
@@ -132,11 +141,12 @@
     $jsonKey = json_decode(
         file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
         true);
-    $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true);
-    hardAssert($result->getUsername() == $jsonKey['client_email'],
-             'invalid email returned');
+    $result = performLargeUnary($stub, $fillUsername = true,
+                                $fillOauthScope = true);
+    hardAssert($result->getUsername() === $jsonKey['client_email'],
+               'invalid email returned');
     hardAssert(strpos($args['oauth_scope'], $result->getOauthScope()) !== false,
-             'invalid oauth scope returned');
+               'invalid oauth scope returned');
 }
 
 /**
@@ -154,9 +164,10 @@
     if (!array_key_exists('default_service_account', $args)) {
         throw new Exception('Missing default_service_account');
     }
-    $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true);
-    hardAssert($args['default_service_account'] == $result->getUsername(),
-             'invalid email returned');
+    $result = performLargeUnary($stub, $fillUsername = true,
+                                $fillOauthScope = true);
+    hardAssert($args['default_service_account'] === $result->getUsername(),
+               'invalid email returned');
 }
 
 /**
@@ -170,9 +181,10 @@
     $jsonKey = json_decode(
         file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
         true);
-    $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true);
-    hardAssert($result->getUsername() == $jsonKey['client_email'],
-             'invalid email returned');
+    $result = performLargeUnary($stub, $fillUsername = true,
+                                $fillOauthScope = true);
+    hardAssert($result->getUsername() === $jsonKey['client_email'],
+               'invalid email returned');
 }
 
 /**
@@ -186,9 +198,10 @@
     $jsonKey = json_decode(
         file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
         true);
-    $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true);
-    hardAssert($result->getUsername() == $jsonKey['client_email'],
-             'invalid email returned');
+    $result = performLargeUnary($stub, $fillUsername = true,
+                                $fillOauthScope = true);
+    hardAssert($result->getUsername() === $jsonKey['client_email'],
+               'invalid email returned');
 }
 
 function updateAuthMetadataCallback($context)
@@ -197,7 +210,13 @@
     $methodName = $context->method_name;
     $auth_credentials = ApplicationDefaultCredentials::getCredentials();
 
-    return $auth_credentials->updateMetadata($metadata = [], $authUri);
+    $metadata = [];
+    $result = $auth_credentials->updateMetadata([], $authUri);
+    foreach ($result as $key => $value) {
+        $metadata[strtolower($key)] = $value;
+    }
+
+    return $metadata;
 }
 
 /**
@@ -212,10 +231,11 @@
         file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
         true);
 
-    $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true,
+    $result = performLargeUnary($stub, $fillUsername = true,
+                                $fillOauthScope = true,
                                 'updateAuthMetadataCallback');
-    hardAssert($result->getUsername() == $jsonKey['client_email'],
-             'invalid email returned');
+    hardAssert($result->getUsername() === $jsonKey['client_email'],
+               'invalid email returned');
 }
 
 /**
@@ -242,9 +262,9 @@
         $call->write($request);
     }
     list($result, $status) = $call->wait();
-    hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
+    hardAssertIfStatusOk($status);
     hardAssert($result->getAggregatedPayloadSize() === 74922,
-              'aggregated_payload_size was incorrect');
+               'aggregated_payload_size was incorrect');
 }
 
 /**
@@ -261,7 +281,7 @@
     foreach ($sizes as $size) {
         $response_parameters = new grpc\testing\ResponseParameters();
         $response_parameters->setSize($size);
-        $request->addResponseParameters($response_parameters);
+        $request->getResponseParameters()[] = $response_parameters;
     }
 
     $call = $stub->StreamingOutputCall($request);
@@ -269,14 +289,14 @@
     foreach ($call->responses() as $value) {
         hardAssert($i < 4, 'Too many responses');
         $payload = $value->getPayload();
-        hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
-                'Payload '.$i.' had the wrong type');
+        hardAssert(
+            $payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
+            'Payload '.$i.' had the wrong type');
         hardAssert(strlen($payload->getBody()) === $sizes[$i],
-                'Response '.$i.' had the wrong length');
+                   'Response '.$i.' had the wrong length');
         $i += 1;
     }
-    hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
-             'Call did not complete successfully');
+    hardAssertIfStatusOk($call->getStatus());
 }
 
 /**
@@ -295,7 +315,7 @@
         $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
         $response_parameters = new grpc\testing\ResponseParameters();
         $response_parameters->setSize($response_lengths[$i]);
-        $request->addResponseParameters($response_parameters);
+        $request->getResponseParameters()[] = $response_parameters;
         $payload = new grpc\testing\Payload();
         $payload->setBody(str_repeat("\0", $request_lengths[$i]));
         $request->setPayload($payload);
@@ -305,15 +325,15 @@
 
         hardAssert($response !== null, 'Server returned too few responses');
         $payload = $response->getPayload();
-        hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
-                'Payload '.$i.' had the wrong type');
+        hardAssert(
+            $payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
+            'Payload '.$i.' had the wrong type');
         hardAssert(strlen($payload->getBody()) === $response_lengths[$i],
-                'Payload '.$i.' had the wrong length');
+                   'Payload '.$i.' had the wrong length');
     }
     $call->writesDone();
     hardAssert($call->read() === null, 'Server returned too many responses');
-    hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
-              'Call did not complete successfully');
+    hardAssertIfStatusOk($call->getStatus());
 }
 
 /**
@@ -326,8 +346,7 @@
     $call = $stub->FullDuplexCall();
     $call->writesDone();
     hardAssert($call->read() === null, 'Server returned too many responses');
-    hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
-             'Call did not complete successfully');
+    hardAssertIfStatusOk($call->getStatus());
 }
 
 /**
@@ -341,7 +360,7 @@
     $call->cancel();
     list($result, $status) = $call->wait();
     hardAssert($status->code === Grpc\STATUS_CANCELLED,
-             'Call status was not CANCELLED');
+               'Call status was not CANCELLED');
 }
 
 /**
@@ -356,7 +375,7 @@
     $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
     $response_parameters = new grpc\testing\ResponseParameters();
     $response_parameters->setSize(31415);
-    $request->addResponseParameters($response_parameters);
+    $request->getResponseParameters()[] = $response_parameters;
     $payload = new grpc\testing\Payload();
     $payload->setBody(str_repeat("\0", 27182));
     $request->setPayload($payload);
@@ -366,7 +385,7 @@
 
     $call->cancel();
     hardAssert($call->getStatus()->code === Grpc\STATUS_CANCELLED,
-             'Call status was not CANCELLED');
+               'Call status was not CANCELLED');
 }
 
 function timeoutOnSleepingServer($stub)
@@ -376,7 +395,7 @@
     $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
     $response_parameters = new grpc\testing\ResponseParameters();
     $response_parameters->setSize(8);
-    $request->addResponseParameters($response_parameters);
+    $request->getResponseParameters()[] = $response_parameters;
     $payload = new grpc\testing\Payload();
     $payload->setBody(str_repeat("\0", 9));
     $request->setPayload($payload);
@@ -385,7 +404,7 @@
     $response = $call->read();
 
     hardAssert($call->getStatus()->code === Grpc\STATUS_DEADLINE_EXCEEDED,
-             'Call status was not DEADLINE_EXCEEDED');
+               'Call status was not DEADLINE_EXCEEDED');
 }
 
 function customMetadata($stub)
@@ -414,19 +433,19 @@
     $initial_metadata = $call->getMetadata();
     hardAssert(array_key_exists($ECHO_INITIAL_KEY, $initial_metadata),
                'Initial metadata does not contain expected key');
-    hardAssert($initial_metadata[$ECHO_INITIAL_KEY][0] ==
-               $ECHO_INITIAL_VALUE,
-               'Incorrect initial metadata value');
+    hardAssert(
+        $initial_metadata[$ECHO_INITIAL_KEY][0] === $ECHO_INITIAL_VALUE,
+        'Incorrect initial metadata value');
 
     list($result, $status) = $call->wait();
-    hardAssert($status->code === Grpc\STATUS_OK,
-               'Call did not complete successfully');
+    hardAssertIfStatusOk($status);
 
     $trailing_metadata = $call->getTrailingMetadata();
     hardAssert(array_key_exists($ECHO_TRAILING_KEY, $trailing_metadata),
                'Trailing metadata does not contain expected key');
-    hardAssert($trailing_metadata[$ECHO_TRAILING_KEY][0] ==
-               $ECHO_TRAILING_VALUE, 'Incorrect trailing metadata value');
+    hardAssert(
+        $trailing_metadata[$ECHO_TRAILING_KEY][0] === $ECHO_TRAILING_VALUE,
+        'Incorrect trailing metadata value');
 
     $streaming_call = $stub->FullDuplexCall($metadata);
 
@@ -435,14 +454,13 @@
     $streaming_call->write($streaming_request);
     $streaming_call->writesDone();
 
-    hardAssert($streaming_call->getStatus()->code === Grpc\STATUS_OK,
-               'Call did not complete successfully');
+    hardAssertIfStatusOk($streaming_call->getStatus());
 
     $streaming_trailing_metadata = $streaming_call->getTrailingMetadata();
     hardAssert(array_key_exists($ECHO_TRAILING_KEY,
                                 $streaming_trailing_metadata),
                'Trailing metadata does not contain expected key');
-    hardAssert($streaming_trailing_metadata[$ECHO_TRAILING_KEY][0] ==
+    hardAssert($streaming_trailing_metadata[$ECHO_TRAILING_KEY][0] ===
                $ECHO_TRAILING_VALUE, 'Incorrect trailing metadata value');
 }
 
@@ -497,7 +515,7 @@
         throw new Exception('Missing argument: --test_case is required');
     }
 
-    if ($args['server_port'] == 443) {
+    if ($args['server_port'] === 443) {
         $server_address = $args['server_host'];
     } else {
         $server_address = $args['server_host'].':'.$args['server_port'];
@@ -539,7 +557,7 @@
 
     if (in_array($test_case, ['service_account_creds',
                               'compute_engine_creds', 'jwt_token_creds', ])) {
-        if ($test_case == 'jwt_token_creds') {
+        if ($test_case === 'jwt_token_creds') {
             $auth_credentials = ApplicationDefaultCredentials::getCredentials();
         } else {
             $auth_credentials = ApplicationDefaultCredentials::getCredentials(
@@ -549,7 +567,7 @@
         $opts['update_metadata'] = $auth_credentials->getUpdateMetadataFunc();
     }
 
-    if ($test_case == 'oauth2_auth_token') {
+    if ($test_case === 'oauth2_auth_token') {
         $auth_credentials = ApplicationDefaultCredentials::getCredentials(
             $args['oauth_scope']
         );
@@ -569,8 +587,9 @@
         $opts['update_metadata'] = $update_metadata;
     }
 
-    if ($test_case == 'unimplemented_method') {
-        $stub = new grpc\testing\UnimplementedServiceClient($server_address, $opts);
+    if ($test_case === 'unimplemented_method') {
+        $stub = new grpc\testing\UnimplementedServiceClient($server_address,
+                                                            $opts);
     } else {
         $stub = new grpc\testing\TestServiceClient($server_address, $opts);
     }
@@ -647,7 +666,8 @@
     return $stub;
 }
 
-if (isset($_SERVER['PHP_SELF']) && preg_match('/interop_client/', $_SERVER['PHP_SELF'])) {
+if (isset($_SERVER['PHP_SELF']) &&
+    preg_match('/interop_client/', $_SERVER['PHP_SELF'])) {
     $args = getopt('', ['server_host:', 'server_port:', 'test_case:',
                         'use_tls::', 'use_test_ca::',
                         'server_host_override:', 'oauth_scope:',
diff --git a/src/php/tests/interop/messages.proto b/src/php/tests/interop/messages.proto
deleted file mode 100644
index 44e3c3b..0000000
--- a/src/php/tests/interop/messages.proto
+++ /dev/null
@@ -1,162 +0,0 @@
-
-// Copyright 2015-2016, 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.
-
-// Message definitions to be used by integration test service definitions.
-
-syntax = "proto2";
-
-package grpc.testing;
-
-// The type of payload that should be returned.
-enum PayloadType {
-  // Compressable text format.
-  COMPRESSABLE = 0;
-
-  // Uncompressable binary format.
-  UNCOMPRESSABLE = 1;
-}
-
-// A block of data, to simply increase gRPC message size.
-message Payload {
-  // The type of data in body.
-  optional PayloadType type = 1 [default = COMPRESSABLE];
-  // Primary contents of payload.
-  optional bytes body = 2;
-}
-
-// A protobuf representation for grpc status. This is used by test
-// clients to specify a status that the server should attempt to return.
-message EchoStatus {
-  optional int32 code = 1;
-  optional string message = 2;
-}
-
-// Unary request.
-message SimpleRequest {
-  // Desired payload type in the response from the server.
-  // If response_type is RANDOM, server randomly chooses one from other formats.
-  optional PayloadType response_type = 1 [default = COMPRESSABLE];
-
-  // Desired payload size in the response from the server.
-  // If response_type is COMPRESSABLE, this denotes the size before compression.
-  optional int32 response_size = 2;
-
-  // Optional input payload sent along with the request.
-  optional Payload payload = 3;
-
-  // Whether SimpleResponse should include username.
-  optional bool fill_username = 4;
-
-  // Whether SimpleResponse should include OAuth scope.
-  optional bool fill_oauth_scope = 5;
-
-  // Whether to request the server to compress the response.
-  optional bool request_compressed_response = 6;
-
-  // Whether server should return a given status
-  optional EchoStatus response_status = 7;
-}
-
-// Unary response, as configured by the request.
-message SimpleResponse {
-  // Payload to increase message size.
-  optional Payload payload = 1;
-  // The user the request came from, for verifying authentication was
-  // successful when the client expected it.
-  optional string username = 2;
-  // OAuth scope.
-  optional string oauth_scope = 3;
-}
-
-// Client-streaming request.
-message StreamingInputCallRequest {
-  // Optional input payload sent along with the request.
-  optional Payload payload = 1;
-
-  // Not expecting any payload from the response.
-}
-
-// Client-streaming response.
-message StreamingInputCallResponse {
-  // Aggregated size of payloads received from the client.
-  optional int32 aggregated_payload_size = 1;
-}
-
-// Configuration for a particular response.
-message ResponseParameters {
-  // Desired payload sizes in responses from the server.
-  // If response_type is COMPRESSABLE, this denotes the size before compression.
-  optional int32 size = 1;
-
-  // Desired interval between consecutive responses in the response stream in
-  // microseconds.
-  optional int32 interval_us = 2;
-}
-
-// Server-streaming request.
-message StreamingOutputCallRequest {
-  // Desired payload type in the response from the server.
-  // If response_type is RANDOM, the payload from each response in the stream
-  // might be of different types. This is to simulate a mixed type of payload
-  // stream.
-  optional PayloadType response_type = 1 [default = COMPRESSABLE];
-
-  // Configuration for each expected response message.
-  repeated ResponseParameters response_parameters = 2;
-
-  // Optional input payload sent along with the request.
-  optional Payload payload = 3;
-
-  // Whether to request the server to compress the response.
-  optional bool request_compressed_response = 6;
-
-  // Whether server should return a given status
-  optional EchoStatus response_status = 7;
-}
-
-// Server-streaming response, as configured by the request and parameters.
-message StreamingOutputCallResponse {
-  // Payload to increase response size.
-  optional Payload payload = 1;
-}
-
-// For reconnect interop test only.
-// Client tells server what reconnection parameters it used.
-message ReconnectParams {
-  optional int32 max_reconnect_backoff_ms = 1;
-}
-
-// For reconnect interop test only.
-// Server tells client whether its reconnects are following the spec and the
-// reconnect backoffs it saw.
-message ReconnectInfo {
-  optional bool passed = 1;
-  repeated int32 backoff_ms = 2;
-}
diff --git a/src/php/tests/interop/test.proto b/src/php/tests/interop/test.proto
deleted file mode 100644
index 57ef30e..0000000
--- a/src/php/tests/interop/test.proto
+++ /dev/null
@@ -1,78 +0,0 @@
-
-// Copyright 2015-2016, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// An integration test service that covers all the method signature permutations
-// of unary/streaming requests/responses.
-syntax = "proto2";
-
-import "empty.proto";
-import "messages.proto";
-
-package grpc.testing;
-
-// A simple service to test the various types of RPCs and experiment with
-// performance with various types of payload.
-service TestService {
-  // One empty request followed by one empty response.
-  rpc EmptyCall(grpc.testing.EmptyMessage) returns (grpc.testing.EmptyMessage);
-
-  // One request followed by one response.
-  rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
-
-  // One request followed by a sequence of responses (streamed download).
-  // The server returns the payload with client desired type and sizes.
-  rpc StreamingOutputCall(StreamingOutputCallRequest)
-      returns (stream StreamingOutputCallResponse);
-
-  // A sequence of requests followed by one response (streamed upload).
-  // The server returns the aggregated size of client payload as the result.
-  rpc StreamingInputCall(stream StreamingInputCallRequest)
-      returns (StreamingInputCallResponse);
-
-  // A sequence of requests with each request served by the server immediately.
-  // As one request could lead to multiple responses, this interface
-  // demonstrates the idea of full duplexing.
-  rpc FullDuplexCall(stream StreamingOutputCallRequest)
-      returns (stream StreamingOutputCallResponse);
-
-  // A sequence of requests followed by a sequence of responses.
-  // The server buffers all the client requests and then serves them in order. A
-  // stream of responses are returned to the client when the server starts with
-  // first request.
-  rpc HalfDuplexCall(stream StreamingOutputCallRequest)
-      returns (stream StreamingOutputCallResponse);
-}
-
-// A simple service NOT implemented at servers so clients can test for
-// that case.
-service UnimplementedService {
-  // A call that no server should implement
-  rpc UnimplementedCall(grpc.testing.EmptyMessage) returns (grpc.testing.EmptyMessage);
-}
diff --git a/src/php/tests/unit_tests/CallCredentials2Test.php b/src/php/tests/unit_tests/CallCredentials2Test.php
index a57e2b9..fa31439 100644
--- a/src/php/tests/unit_tests/CallCredentials2Test.php
+++ b/src/php/tests/unit_tests/CallCredentials2Test.php
@@ -132,4 +132,68 @@
         unset($call);
         unset($server_call);
     }
+
+    public function invalidKeyCallbackFunc($context)
+    {
+        $this->assertTrue(is_string($context->service_url));
+        $this->assertTrue(is_string($context->method_name));
+
+        return ['K1' => ['v1']];
+    }
+
+    public function testCallbackWithInvalidKey()
+    {
+        $deadline = Grpc\Timeval::infFuture();
+        $status_text = 'xyz';
+        $call = new Grpc\Call($this->channel,
+                              '/abc/dummy_method',
+                              $deadline,
+                              $this->host_override);
+
+        $call_credentials = Grpc\CallCredentials::createFromPlugin(
+            array($this, 'invalidKeyCallbackFunc'));
+        $call->setCredentials($call_credentials);
+
+        $event = $call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
+            Grpc\OP_RECV_STATUS_ON_CLIENT => true,
+        ]);
+
+        $this->assertTrue($event->send_metadata);
+        $this->assertTrue($event->send_close);
+        $this->assertTrue($event->status->code == Grpc\STATUS_UNAUTHENTICATED);
+    }
+
+    public function invalidReturnCallbackFunc($context)
+    {
+        $this->assertTrue(is_string($context->service_url));
+        $this->assertTrue(is_string($context->method_name));
+
+        return 'a string';
+    }
+
+    public function testCallbackWithInvalidReturnValue()
+    {
+        $deadline = Grpc\Timeval::infFuture();
+        $status_text = 'xyz';
+        $call = new Grpc\Call($this->channel,
+                              '/abc/dummy_method',
+                              $deadline,
+                              $this->host_override);
+
+        $call_credentials = Grpc\CallCredentials::createFromPlugin(
+            array($this, 'invalidReturnCallbackFunc'));
+        $call->setCredentials($call_credentials);
+
+        $event = $call->startBatch([
+            Grpc\OP_SEND_INITIAL_METADATA => [],
+            Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
+            Grpc\OP_RECV_STATUS_ON_CLIENT => true,
+        ]);
+
+        $this->assertTrue($event->send_metadata);
+        $this->assertTrue($event->send_close);
+        $this->assertTrue($event->status->code == Grpc\STATUS_UNAUTHENTICATED);
+    }
 }
diff --git a/src/php/tests/unit_tests/CallCredentials3Test.php b/src/php/tests/unit_tests/CallCredentials3Test.php
deleted file mode 100644
index 8f5e109..0000000
--- a/src/php/tests/unit_tests/CallCredentials3Test.php
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-class CallCredentials3Test extends PHPUnit_Framework_TestCase
-{
-    public function setUp()
-    {
-        $this->credentials = Grpc\ChannelCredentials::createSsl(
-            file_get_contents(dirname(__FILE__).'/../data/ca.pem'));
-        $server_credentials = Grpc\ServerCredentials::createSsl(
-            null,
-            file_get_contents(dirname(__FILE__).'/../data/server1.key'),
-            file_get_contents(dirname(__FILE__).'/../data/server1.pem'));
-        $this->server = new Grpc\Server();
-        $this->port = $this->server->addSecureHttp2Port('0.0.0.0:0',
-                                              $server_credentials);
-        $this->server->start();
-        $this->host_override = 'foo.test.google.fr';
-        $this->channel = new Grpc\Channel(
-            'localhost:'.$this->port,
-            [
-            'grpc.ssl_target_name_override' => $this->host_override,
-            'grpc.default_authority' => $this->host_override,
-            'credentials' => $this->credentials,
-            ]
-        );
-    }
-
-    public function tearDown()
-    {
-        unset($this->channel);
-        unset($this->server);
-    }
-
-    public function callbackFunc($context)
-    {
-        $this->assertTrue(is_string($context->service_url));
-        $this->assertTrue(is_string($context->method_name));
-
-        return ['k1' => ['v1'], 'k2' => ['v2']];
-    }
-
-    public function testCreateFromPlugin()
-    {
-        $deadline = Grpc\Timeval::infFuture();
-        $status_text = 'xyz';
-        $call = new Grpc\Call($this->channel,
-                              '/abc/dummy_method',
-                              $deadline,
-                              $this->host_override);
-
-        $call_credentials = Grpc\CallCredentials::createFromPlugin(
-            [$this, 'callbackFunc']);
-        $call->setCredentials($call_credentials);
-
-        $event = $call->startBatch([
-            Grpc\OP_SEND_INITIAL_METADATA => [],
-            Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
-        ]);
-
-        $this->assertTrue($event->send_metadata);
-        $this->assertTrue($event->send_close);
-
-        $event = $this->server->requestCall();
-
-        $this->assertTrue(is_array($event->metadata));
-        $metadata = $event->metadata;
-        $this->assertTrue(array_key_exists('k1', $metadata));
-        $this->assertTrue(array_key_exists('k2', $metadata));
-        $this->assertSame($metadata['k1'], ['v1']);
-        $this->assertSame($metadata['k2'], ['v2']);
-
-        $this->assertSame('/abc/dummy_method', $event->method);
-        $server_call = $event->call;
-
-        $event = $server_call->startBatch([
-            Grpc\OP_SEND_INITIAL_METADATA => [],
-            Grpc\OP_SEND_STATUS_FROM_SERVER => [
-                'metadata' => [],
-                'code' => Grpc\STATUS_OK,
-                'details' => $status_text,
-            ],
-            Grpc\OP_RECV_CLOSE_ON_SERVER => true,
-        ]);
-
-        $this->assertTrue($event->send_metadata);
-        $this->assertTrue($event->send_status);
-        $this->assertFalse($event->cancelled);
-
-        $event = $call->startBatch([
-            Grpc\OP_RECV_INITIAL_METADATA => true,
-            Grpc\OP_RECV_STATUS_ON_CLIENT => true,
-        ]);
-
-        $this->assertSame([], $event->metadata);
-        $status = $event->status;
-        $this->assertSame([], $status->metadata);
-        $this->assertSame(Grpc\STATUS_OK, $status->code);
-        $this->assertSame($status_text, $status->details);
-
-        unset($call);
-        unset($server_call);
-    }
-}
diff --git a/src/php/tests/unit_tests/CallTest.php b/src/php/tests/unit_tests/CallTest.php
index d736f51..1205f0c 100644
--- a/src/php/tests/unit_tests/CallTest.php
+++ b/src/php/tests/unit_tests/CallTest.php
@@ -113,7 +113,7 @@
     /**
      * @expectedException InvalidArgumentException
      */
-    public function testInvalidMetadataKey()
+    public function testInvalidStartBatchKey()
     {
         $batch = [
             'invalid' => ['key1' => 'value1'],
@@ -124,6 +124,28 @@
     /**
      * @expectedException InvalidArgumentException
      */
+    public function testInvalidMetadataStrKey()
+    {
+        $batch = [
+            Grpc\OP_SEND_INITIAL_METADATA => ['Key' => ['value1', 'value2']],
+        ];
+        $result = $this->call->startBatch($batch);
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    public function testInvalidMetadataIntKey()
+    {
+        $batch = [
+            Grpc\OP_SEND_INITIAL_METADATA => [1 => ['value1', 'value2']],
+        ];
+        $result = $this->call->startBatch($batch);
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
     public function testInvalidMetadataInnerValue()
     {
         $batch = [
diff --git a/src/proto/census/census.proto b/src/proto/census/census.proto
index c869d85..c2a594b 100644
--- a/src/proto/census/census.proto
+++ b/src/proto/census/census.proto
@@ -33,12 +33,12 @@
 
 // All the census protos.
 //
-// Nomenclature note: capitalized names below (like Metric) are protos.
+// Nomenclature note: capitalized names below (like Resource) are protos.
 //
-// Census lets you define a Metric - something which can be measured, like the
+// Census lets you define a Resource - something which can be measured, like the
 // latency of an RPC, the number of CPU cycles spent on an operation, or
 // anything else you care to measure. You can record individual instances of
-// measurements (a double value) for every metric of interest. These
+// measurements (a double value) for every Resource of interest. These
 // individual measurements are aggregated together into an Aggregation. There
 // are two Aggregation types available: Distribution (describes the
 // distribution of all measurements, possibly with a histogram) and
@@ -47,8 +47,8 @@
 //
 // You can define how your stats are broken down by Tag values and which
 // Aggregations to use through a View. The corresponding combination of
-// Metric/View/Aggregation which is available to census clients is called a
-// ViewAggregation.
+// Resource/View/Aggregation which is available to census clients is called a
+// Metric.
 
 
 // The following two types are copied from
@@ -85,26 +85,23 @@
   int32 nanos = 2;
 }
 
-// Describes a metric
-message Metric {
-  // name of metric, e.g. rpc_latency, cpu.
+// Describes a Resource.
+message Resource {
+  // name of resource, e.g. rpc_latency, cpu. Must be unique.
   string name = 1;
 
-  // More detailed description of the metric, used in documentation.
+  // More detailed description of the resource, used in documentation.
   string description = 2;
 
   // Fundamental units of measurement supported by Census
   // TODO(aveitch): expand this to include other S.I. units?
-  message BasicUnit {
-    enum Measure {
-      UNKNOWN = 0;
-      BITS = 1;
-      BYTES = 2;
-      SECS = 3;
-      CORES = 4;
-      MAX_UNITS = 5;
-    }
-    Measure type = 1;
+  enum BasicUnit {
+    UNKNOWN = 0;
+    BITS = 1;
+    BYTES = 2;
+    SECS = 3;
+    CORES = 4;
+    MAX_UNITS = 5;
   }
 
   // MeasurementUnit lets you build compound units of the form
@@ -124,7 +121,7 @@
   //     denominator: SECS
   //     denominator: SECS
   //
-  // To specify multiples (in power of 10) units, specify a non-zero prefix
+  // To specify multiples (in power of 10) of units, specify a non-zero prefix
   // value, for example:
   //
   // - MB/s (i.e. megabytes / s):
@@ -141,32 +138,40 @@
     repeated BasicUnit denominator = 3;
   }
 
-  // The units in which the Metric value is reported.
+  // The units in which Resource values are measured.
   MeasurementUnit unit = 3;
-
-  // Metrics will be assigned an ID when registered. Invalid if <= 0.
-  int32 id = 4;
 }
 
-// An Aggregation summarizes a series of individual Metric measurements, an
+// An Aggregation summarizes a series of individual Resource measurements, an
 // AggregationDescriptor describes an Aggregation.
 message AggregationDescriptor {
-  // At most one set of options. If neither option is set, a default type
-  // of Distribution (without a histogram component) will be used.
+  enum AggregationType {
+    // Unspecified. Should not be used.
+    UNKNOWN = 0;
+    // A count of measurements made.
+    COUNT = 1;
+    // A Distribution.
+    DISTRIBUTION = 2;
+    // Counts over fixed time intervals.
+    INTERVAL = 3;
+  }
+  // The type of Aggregation.
+  AggregationType type = 1;
+
+  // At most one set of options. It is illegal to specifiy an option for
+  // COUNT Aggregations. interval_boundaries must be set for INTERVAL types.
+  // bucket_boundaries are optional for DISTRIBUTION types.
   oneof options {
-    // Defines the histogram bucket boundaries for Distributions.
-    BucketBoundaries bucket_boundaries = 1;
+    // Defines histogram bucket boundaries for Distributions.
+    BucketBoundaries bucket_boundaries = 2;
     // Defines the time windows to record for IntervalStats.
-    IntervalBoundaries interval_boundaries = 2;
+    IntervalBoundaries interval_boundaries = 3;
   }
 
   // A Distribution may optionally contain a histogram of the values in the
-  // population. The bucket boundaries for that histogram is described by
-  // `bucket_boundaries`.
-  //
-  // Describes histogram bucket boundaries. Defines `size(bounds) + 1` (= N)
-  // buckets (for size(bounds) >= 1; if size(bounds) == 0, then no histogram
-  // will be defined. The boundaries for bucket index i are:
+  // population. The bucket boundaries for that histogram are described by
+  // `bucket_boundaries`. This defines `size(bounds) + 1` (= N) buckets. The
+  // boundaries for bucket index i are:
   //
   // [-infinity, bounds[i]) for i == 0
   // [bounds[i-1], bounds[i]) for 0 < i < N-2
@@ -196,8 +201,8 @@
 // a specified set of histogram buckets, as defined in
 // Aggregation.bucket_options.
 //
-// The summary statistics are the count, mean, sum of the squared deviation from
-// the mean, the minimum, and the maximum of the set of population of values.
+// The summary statistics are the count, mean, minimum, and the maximum of the
+// set of population of values.
 //
 // Although it is not forbidden, it is generally a bad idea to include
 // non-finite values (infinities or NaNs) in the population of values, as this
@@ -243,7 +248,7 @@
 message IntervalStats {
   // Summary statistic over a single time window.
   message Window {
-    // The window duration.
+    // The window duration. Must be positive.
     Duration window_size = 1;
     // The number of measurements in this window.
     int64 count = 2;
@@ -251,7 +256,7 @@
     double mean = 3;
   }
 
-  // Full set of windows for this metric.
+  // Full set of windows for this aggregation.
   repeated Window window = 1;
 }
 
@@ -264,24 +269,24 @@
 // A View specifies an Aggregation and a set of tag keys. The Aggregation will
 // be broken down by the unique set of matching tag values for each measurement.
 message View {
-  // Name of view.
+  // Name of view. Must be unique.
   string name = 1;
 
   // More detailed description, for documentation purposes.
   string description = 2;
 
-  // ID of Metric to associate with this View.
-  int32 metric_id = 3;
+  // Name of Resource to be broken down for this view.
+  string resource_name = 3;
 
   // Aggregation type to associate with this View.
   AggregationDescriptor aggregation = 4;
 
-  // Tag keys to match with a given Metric. If no keys are specified, then all
-  // stats for the Metric are recorded. Keys must be unique.
+  // Tag keys to match with a given Resource measurement. If no keys are
+  // specified, then all stats are recorded. Keys must be unique.
   repeated string tag_key = 5;
 }
 
-// An Aggregation summarizes a series of individual Metric measures.
+// An Aggregation summarizes a series of individual Resource measurements.
 message Aggregation {
   // Name of this aggregation.
   string name = 1;
@@ -291,23 +296,27 @@
 
   // The data for this Aggregation.
   oneof data {
-    Distribution distribution = 3;
-    IntervalStats interval_stats = 4;
+    uint64 count = 3;
+    Distribution distribution = 4;
+    IntervalStats interval_stats = 5;
   }
 
   // Tags associated with this Aggregation.
-  repeated Tag tag = 5;
+  repeated Tag tag = 6;
 }
 
-// A ViewAggregations represents all the Aggregations for a particular view.
-message ViewAggregations {
+// A Metric represents all the Aggregations for a particular view.
+message Metric {
+  // View associated with this Metric.
+  string view_name = 1;
+
   // Aggregations - each will have a unique set of tag values for the tag_keys
   // associated with the corresponding View.
-  repeated Aggregation aggregation = 1;
+  repeated Aggregation aggregation = 2;
 
-  // Start and end timestamps over which the value was accumulated. These
+  // Start and end timestamps over which the metric was accumulated. These
   // values are not relevant/defined for IntervalStats aggregations, which are
   // always accumulated over a fixed time period.
-  Timestamp start = 2;
-  Timestamp end = 3;
+  Timestamp start = 3;
+  Timestamp end = 4;
 }
diff --git a/src/proto/census/trace_context.options b/src/proto/census/trace_context.options
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/proto/census/trace_context.options
diff --git a/src/proto/census/trace_context.proto b/src/proto/census/trace_context.proto
new file mode 100644
index 0000000..a5d5a95
--- /dev/null
+++ b/src/proto/census/trace_context.proto
@@ -0,0 +1,48 @@
+// Copyright 2016, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package google.trace;
+
+// A TraceId uniquely represents a single Trace. It is a 128-bit nonce.
+message TraceId {
+  fixed64 hi = 1;
+  fixed64 lo = 2;
+}
+
+// Tracing information that is propagated with RPC's.
+message TraceContext {
+  // Trace identifer. Must be present.
+  TraceId trace_id = 1;
+  // ID of parent (client) span. Must be present.
+  fixed64 span_id = 2;
+  // true if this trace is sampled.
+  bool is_sampled = 3;
+}
diff --git a/src/proto/grpc/lb/v1/load_balancer.options b/src/proto/grpc/lb/v1/load_balancer.options
index d903669..a9398d5 100644
--- a/src/proto/grpc/lb/v1/load_balancer.options
+++ b/src/proto/grpc/lb/v1/load_balancer.options
@@ -1,6 +1,6 @@
 grpc.lb.v1.InitialLoadBalanceRequest.name max_size:128
 grpc.lb.v1.InitialLoadBalanceResponse.client_config max_size:64
 grpc.lb.v1.InitialLoadBalanceResponse.load_balancer_delegate max_size:64
-grpc.lb.v1.Server.ip_address max_size:46
-grpc.lb.v1.Server.load_balance_token max_size:64
+grpc.lb.v1.Server.ip_address max_size:16
+grpc.lb.v1.Server.load_balance_token max_size:65
 load_balancer.proto no_unions:true
diff --git a/src/proto/grpc/lb/v1/load_balancer.proto b/src/proto/grpc/lb/v1/load_balancer.proto
index 1bcad0b..210fba1 100644
--- a/src/proto/grpc/lb/v1/load_balancer.proto
+++ b/src/proto/grpc/lb/v1/load_balancer.proto
@@ -32,7 +32,6 @@
 package grpc.lb.v1;
 
 message Duration {
-
   // Signed seconds of the span of time. Must be from -315,576,000,000
   // to +315,576,000,000 inclusive.
   int64 seconds = 1;
@@ -93,21 +92,16 @@
 }
 
 message InitialLoadBalanceResponse {
-  oneof initial_response_type {
-    // TODO(zhangkun83): ClientConfig not yet defined
-    //ClientConfig client_config = 1;
-
-    // This is an application layer redirect that indicates the client should
-    // use the specified server for load balancing. When this field is set in
-    // the response, the client should open a separate connection to the
-    // load_balancer_delegate and call the BalanceLoad method.
-    string load_balancer_delegate = 2;
-  }
+  // This is an application layer redirect that indicates the client should use
+  // the specified server for load balancing. When this field is non-empty in
+  // the response, the client should open a separate connection to the
+  // load_balancer_delegate and call the BalanceLoad method.
+  string load_balancer_delegate = 1;
 
   // This interval defines how often the client should send the client stats
   // to the load balancer. Stats should only be reported when the duration is
   // positive.
-  Duration client_stats_report_interval = 3;
+  Duration client_stats_report_interval = 2;
 }
 
 message ServerList {
@@ -125,14 +119,17 @@
 }
 
 message Server {
-  // A resolved address and port for the server. The IP address string may
+  // A resolved address for the server, serialized in network-byte-order. It may
   // either be an IPv4 or IPv6 address.
-  string ip_address = 1;
+  bytes ip_address = 1;
+
+  // A resolved port number for the server.
   int32 port = 2;
 
-  // An opaque token that is passed from the client to the server in metadata.
-  // The server may expect this token to indicate that the request from the
-  // client was load balanced.
+  // An opaque but printable token given to the frontend for each pick. All
+  // frontend requests for that pick must include the token in its initial
+  // metadata. The token is used by the backend to verify the request and to
+  // allow the backend to report load to the gRPC LB system.
   string load_balance_token = 3;
 
   // Indicates whether this particular request should be dropped by the client
diff --git a/src/proto/grpc/testing/echo.proto b/src/proto/grpc/testing/echo.proto
index c596aab..ca60a41 100644
--- a/src/proto/grpc/testing/echo.proto
+++ b/src/proto/grpc/testing/echo.proto
@@ -42,7 +42,7 @@
   rpc Unimplemented(EchoRequest) returns (EchoResponse);
 }
 
-service UnimplementedService {
+service UnimplementedEchoService {
   rpc Unimplemented(EchoRequest) returns (EchoResponse);
 }
 
diff --git a/src/python/grpcio/_spawn_patch.py b/src/python/grpcio/_spawn_patch.py
new file mode 100644
index 0000000..24306f0
--- /dev/null
+++ b/src/python/grpcio/_spawn_patch.py
@@ -0,0 +1,73 @@
+# Copyright 2016, 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.
+
+"""Patches the spawn() command for windows compilers.
+
+Windows has an 8191 character command line limit, but some compilers
+support an @command_file directive where command_file is a file
+containing the full command line.
+"""
+
+from distutils import ccompiler
+import os
+import os.path
+import shutil
+import sys
+import tempfile
+
+MAX_COMMAND_LENGTH = 8191
+
+_classic_spawn = ccompiler.CCompiler.spawn
+
+def _commandfile_spawn(self, command):
+  command_length = sum([len(arg) for arg in command])
+  if os.name == 'nt' and command_length > MAX_COMMAND_LENGTH:
+    # Even if this command doesn't support the @command_file, it will
+    # fail as is so we try blindly
+    print('Command line length exceeded, using command file')
+    print(' '.join(command))
+    temporary_directory = tempfile.mkdtemp()
+    command_filename = os.path.abspath(
+    os.path.join(temporary_directory, 'command'))
+    with open(command_filename, 'w') as command_file:
+      escaped_args = ['"' + arg.replace('\\', '\\\\') + '"' for arg in command[1:]]
+      command_file.write(' '.join(escaped_args))
+    modified_command = command[:1] + ['@{}'.format(command_filename)]
+    try:
+      _classic_spawn(self, modified_command)
+    finally:
+      shutil.rmtree(temporary_directory)
+  else:
+    _classic_spawn(self, command)
+
+
+def monkeypatch_spawn():
+  """Monkeypatching is dumb, but it's either that or we become maintainers of
+     something much, much bigger."""
+  ccompiler.CCompiler.spawn = _commandfile_spawn
diff --git a/src/python/grpcio/_unixccompiler_patch.py b/src/python/grpcio/_unixccompiler_patch.py
deleted file mode 100644
index 894c3ef..0000000
--- a/src/python/grpcio/_unixccompiler_patch.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# Copyright 2016, 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.
-
-"""Covers inadequacies in distutils."""
-
-from distutils import ccompiler
-from distutils import errors
-from distutils import unixccompiler
-import os
-import os.path
-import shlex
-import shutil
-import sys
-import tempfile
-
-def _unix_commandfile_spawn(self, command):
-  """Wrapper around distutils.util.spawn that attempts to use command files.
-
-  Meant to replace the CCompiler method `spawn` on UnixCCompiler and its
-  derivatives (e.g. the MinGW32 compiler).
-
-  Some commands like `gcc` (and friends like `clang`) support command files to
-  work around shell command length limits.
-  """
-  # Sometimes distutils embeds the executables as full strings including some
-  # hard-coded flags rather than as lists.
-  command = list(shlex.split(command[0])) + list(command[1:])
-  command_base = os.path.basename(command[0].strip())
-  if command_base == 'ccache':
-    command_base = command[:2]
-    command_args = command[2:]
-  elif command_base.startswith('ccache') or command_base in ['gcc', 'clang', 'clang++', 'g++']:
-    command_base = command[:1]
-    command_args = command[1:]
-  else:
-    return ccompiler.CCompiler.spawn(self, command)
-  temporary_directory = tempfile.mkdtemp()
-  command_filename = os.path.abspath(os.path.join(temporary_directory, 'command'))
-  with open(command_filename, 'w') as command_file:
-    escaped_args = [arg.replace('\\', '\\\\') for arg in command_args]
-    command_file.write(' '.join(escaped_args))
-  modified_command = command_base + ['@{}'.format(command_filename)]
-  result = ccompiler.CCompiler.spawn(self, modified_command)
-  shutil.rmtree(temporary_directory)
-  return result
-
-
-def monkeypatch_unix_compiler():
-  """Monkeypatching is dumb, but it's either that or we become maintainers of
-     something much, much bigger."""
-  unixccompiler.UnixCCompiler.spawn = _unix_commandfile_spawn
diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py
index d36ac23..ea3b6f3 100644
--- a/src/python/grpcio/commands.py
+++ b/src/python/grpcio/commands.py
@@ -53,6 +53,7 @@
 GRPC_STEM = os.path.abspath(PYTHON_STEM + '../../../../')
 PROTO_STEM = os.path.join(GRPC_STEM, 'src', 'proto')
 PROTO_GEN_STEM = os.path.join(GRPC_STEM, 'src', 'python', 'gens')
+CYTHON_STEM = os.path.join(PYTHON_STEM, 'grpc', '_cython')
 
 CONF_PY_ADDENDUM = """
 extensions.append('sphinx.ext.napoleon')
@@ -244,7 +245,7 @@
     extensions,
     include_path=[
       include_dir for extension in extensions for include_dir in extension.include_dirs
-    ],
+    ] + [CYTHON_STEM],
     compiler_directives=cython_compiler_directives
   )
 
diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py
index 513839d..526bd9e 100644
--- a/src/python/grpcio/grpc/__init__.py
+++ b/src/python/grpcio/grpc/__init__.py
@@ -649,6 +649,10 @@
 
     Args:
       method: The name of the RPC method.
+      request_serializer: Optional behaviour for serializing the request
+        message. Request goes unserialized in case None is passed.
+      response_deserializer: Optional behaviour for deserializing the response
+        message. Response goes undeserialized in case None is passed.
 
     Returns:
       A UnaryUnaryMultiCallable value for the named unary-unary method.
@@ -662,6 +666,10 @@
 
     Args:
       method: The name of the RPC method.
+      request_serializer: Optional behaviour for serializing the request
+        message. Request goes unserialized in case None is passed.
+      response_deserializer: Optional behaviour for deserializing the response
+        message. Response goes undeserialized in case None is passed.
 
     Returns:
       A UnaryStreamMultiCallable value for the name unary-stream method.
@@ -675,6 +683,10 @@
 
     Args:
       method: The name of the RPC method.
+      request_serializer: Optional behaviour for serializing the request
+        message. Request goes unserialized in case None is passed.
+      response_deserializer: Optional behaviour for deserializing the response
+        message. Response goes undeserialized in case None is passed.
 
     Returns:
       A StreamUnaryMultiCallable value for the named stream-unary method.
@@ -688,6 +700,10 @@
 
     Args:
       method: The name of the RPC method.
+      request_serializer: Optional behaviour for serializing the request
+        message. Request goes unserialized in case None is passed.
+      response_deserializer: Optional behaviour for deserializing the response
+        message. Response goes undeserialized in case None is passed.
 
     Returns:
       A StreamStreamMultiCallable value for the named stream-stream method.
@@ -1078,7 +1094,7 @@
 
   Args:
     access_token: A string to place directly in the http request
-      authorization header, ie "Authorization: Bearer <access_token>".
+      authorization header, ie "authorization: Bearer <access_token>".
 
   Returns:
     A CallCredentials.
@@ -1189,11 +1205,11 @@
     A Channel to the target through which RPCs may be conducted.
   """
   from grpc import _channel
-  return _channel.Channel(target, options, None)
+  return _channel.Channel(target, () if options is None else options, None)
 
 
 def secure_channel(target, credentials, options=None):
-  """Creates an insecure Channel to a server.
+  """Creates a secure Channel to a server.
 
   Args:
     target: The target to which to connect.
@@ -1205,10 +1221,11 @@
     A Channel to the target through which RPCs may be conducted.
   """
   from grpc import _channel
-  return _channel.Channel(target, options, credentials._credentials)
+  return _channel.Channel(target, () if options is None else options,
+                          credentials._credentials)
 
 
-def server(thread_pool, handlers=None):
+def server(thread_pool, handlers=None, options=None):
   """Creates a Server with which RPCs can be serviced.
 
   Args:
@@ -1219,12 +1236,15 @@
       only handlers the server will use to service RPCs; other handlers may
       later be added by calling add_generic_rpc_handlers any time before the
       returned Server is started.
+    options: A sequence of string-value pairs according to which to configure
+      the created server.
 
   Returns:
     A Server with which RPCs can be serviced.
   """
   from grpc import _server
-  return _server.Server(thread_pool, () if handlers is None else handlers)
+  return _server.Server(thread_pool, () if handlers is None else handlers,
+                        () if options is None else options)
 
 
 ###################################  __all__  #################################
diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py
index 3117dd1..53a2672 100644
--- a/src/python/grpcio/grpc/_channel.py
+++ b/src/python/grpcio/grpc/_channel.py
@@ -842,18 +842,8 @@
 
 
 def _options(options):
-  if options is None:
-    pairs = ((cygrpc.ChannelArgKey.primary_user_agent_string, _USER_AGENT),)
-  else:
-    pairs = list(options) + [
-        (cygrpc.ChannelArgKey.primary_user_agent_string, _USER_AGENT)]
-  encoded_pairs = [
-      (_common.encode(arg_name), arg_value) if isinstance(arg_value, int)
-      else (_common.encode(arg_name), _common.encode(arg_value))
-      for arg_name, arg_value in pairs]
-  return cygrpc.ChannelArgs([
-      cygrpc.ChannelArg(arg_name, arg_value)
-      for arg_name, arg_value in encoded_pairs])
+  return list(options) + [
+      (cygrpc.ChannelArgKey.primary_user_agent_string, _USER_AGENT)]
 
 
 class Channel(grpc.Channel):
@@ -867,7 +857,8 @@
       credentials: A cygrpc.ChannelCredentials or None.
     """
     self._channel = cygrpc.Channel(
-        _common.encode(target), _options(options), credentials)
+        _common.encode(target), _common.channel_args(_options(options)),
+        credentials)
     self._call_state = _ChannelCallState(self._channel)
     self._connectivity_state = _ChannelConnectivityState(self._channel)
 
diff --git a/src/python/grpcio/grpc/_common.py b/src/python/grpcio/grpc/_common.py
index 4d7d521..cc0984c 100644
--- a/src/python/grpcio/grpc/_common.py
+++ b/src/python/grpcio/grpc/_common.py
@@ -94,6 +94,16 @@
       return b.decode('latin1')
 
 
+def channel_args(options):
+  channel_args = []
+  for key, value in options:
+    if isinstance(value, six.string_types):
+      channel_args.append(cygrpc.ChannelArg(encode(key), encode(value)))
+    else:
+      channel_args.append(cygrpc.ChannelArg(encode(key), value))
+  return cygrpc.ChannelArgs(channel_args)
+
+
 def cygrpc_metadata(application_metadata):
   return _EMPTY_METADATA if application_metadata is None else cygrpc.Metadata(
       cygrpc.Metadatum(encode(key), encode(value))
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx.pxi
index ba60986..cc3bd7a 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx.pxi
@@ -34,6 +34,7 @@
 
   def __cinit__(self):
     # Create an *empty* call
+    grpc_init()
     self.c_call = NULL
     self.references = []
 
@@ -106,6 +107,7 @@
   def __dealloc__(self):
     if self.c_call != NULL:
       grpc_call_destroy(self.c_call)
+    grpc_shutdown()
 
   # The object *should* always be valid from Python. Used for debugging.
   @property
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
index 5416401..73d1ff7 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
@@ -32,14 +32,16 @@
 
 cdef class Channel:
 
-  def __cinit__(self, bytes target, ChannelArgs arguments=None,
+  def __cinit__(self, bytes target, ChannelArgs arguments,
                 ChannelCredentials channel_credentials=None):
+    grpc_init()
     cdef grpc_channel_args *c_arguments = NULL
     cdef char *c_target = NULL
     self.c_channel = NULL
     self.references = []
-    if arguments is not None:
+    if len(arguments) > 0:
       c_arguments = &arguments.c_args
+      self.references.append(arguments)
     c_target = target
     if channel_credentials is None:
       with nogil:
@@ -103,3 +105,4 @@
   def __dealloc__(self):
     if self.c_channel != NULL:
       grpc_channel_destroy(self.c_channel)
+    grpc_shutdown()
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
index 5955021..a258ba4 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
@@ -38,6 +38,7 @@
 cdef class CompletionQueue:
 
   def __cinit__(self):
+    grpc_init()
     with nogil:
       self.c_completion_queue = grpc_completion_queue_create(NULL)
     self.is_shutting_down = False
@@ -129,3 +130,4 @@
             self.c_completion_queue, c_deadline, NULL)
         self._interpret_event(event)
       grpc_completion_queue_destroy(self.c_completion_queue)
+    grpc_shutdown()
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
index 035ac49..04872b9 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
@@ -33,6 +33,7 @@
 cdef class ChannelCredentials:
 
   def __cinit__(self):
+    grpc_init()
     self.c_credentials = NULL
     self.c_ssl_pem_key_cert_pair.private_key = NULL
     self.c_ssl_pem_key_cert_pair.certificate_chain = NULL
@@ -47,11 +48,13 @@
   def __dealloc__(self):
     if self.c_credentials != NULL:
       grpc_channel_credentials_release(self.c_credentials)
+    grpc_shutdown()
 
 
 cdef class CallCredentials:
 
   def __cinit__(self):
+    grpc_init()
     self.c_credentials = NULL
     self.references = []
 
@@ -64,17 +67,20 @@
   def __dealloc__(self):
     if self.c_credentials != NULL:
       grpc_call_credentials_release(self.c_credentials)
+    grpc_shutdown()
 
 
 cdef class ServerCredentials:
 
   def __cinit__(self):
+    grpc_init()
     self.c_credentials = NULL
     self.references = []
 
   def __dealloc__(self):
     if self.c_credentials != NULL:
       grpc_server_credentials_release(self.c_credentials)
+    grpc_shutdown()
 
 
 cdef class CredentialsMetadataPlugin:
@@ -90,6 +96,7 @@
         successful).
       name (bytes): Plugin name.
     """
+    grpc_init()
     if not callable(plugin_callback):
       raise ValueError('expected callable plugin_callback')
     self.plugin_callback = plugin_callback
@@ -105,10 +112,14 @@
     cpython.Py_INCREF(self)
     return result
 
+  def __dealloc__(self):
+    grpc_shutdown()
+
 
 cdef class AuthMetadataContext:
 
   def __cinit__(self):
+    grpc_init()
     self.context.service_url = NULL
     self.context.method_name = NULL
 
@@ -120,6 +131,9 @@
   def method_name(self):
     return self.context.method_name
 
+  def __dealloc__(self):
+    grpc_shutdown()
+
 
 cdef void plugin_get_metadata(
     void *state, grpc_auth_metadata_context context,
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
index 42fced6..ad20c94 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
@@ -135,7 +135,8 @@
   const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING
   const char *GRPC_ARG_ENABLE_CENSUS
   const char *GRPC_ARG_MAX_CONCURRENT_STREAMS
-  const char *GRPC_ARG_MAX_MESSAGE_LENGTH
+  const char *GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH
+  const char *GRPC_ARG_MAX_SEND_MESSAGE_LENGTH
   const char *GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER
   const char *GRPC_ARG_DEFAULT_AUTHORITY
   const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
index 54b3d00..5a11a08 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
@@ -39,7 +39,8 @@
 class ChannelArgKey:
   enable_census = GRPC_ARG_ENABLE_CENSUS
   max_concurrent_streams = GRPC_ARG_MAX_CONCURRENT_STREAMS
-  max_message_length = GRPC_ARG_MAX_MESSAGE_LENGTH
+  max_receive_message_length = GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH
+  max_send_message_length = GRPC_ARG_MAX_SEND_MESSAGE_LENGTH
   http2_initial_sequence_number = GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER
   default_authority = GRPC_ARG_DEFAULT_AUTHORITY
   primary_user_agent_string = GRPC_ARG_PRIMARY_USER_AGENT_STRING
@@ -176,12 +177,14 @@
 cdef class CallDetails:
 
   def __cinit__(self):
+    grpc_init()
     with nogil:
       grpc_call_details_init(&self.c_details)
 
   def __dealloc__(self):
     with nogil:
       grpc_call_details_destroy(&self.c_details)
+    grpc_shutdown()
 
   @property
   def method(self):
@@ -232,6 +235,7 @@
 cdef class ByteBuffer:
 
   def __cinit__(self, bytes data):
+    grpc_init()
     if data is None:
       self.c_byte_buffer = NULL
       return
@@ -288,6 +292,7 @@
   def __dealloc__(self):
     if self.c_byte_buffer != NULL:
       grpc_byte_buffer_destroy(self.c_byte_buffer)
+    grpc_shutdown()
 
 
 cdef class SslPemKeyCertPair:
@@ -319,6 +324,7 @@
 cdef class ChannelArgs:
 
   def __cinit__(self, args):
+    grpc_init()
     self.args = list(args)
     for arg in self.args:
       if not isinstance(arg, ChannelArg):
@@ -333,6 +339,7 @@
   def __dealloc__(self):
     with nogil:
       gpr_free(self.c_args.arguments)
+    grpc_shutdown()
 
   def __len__(self):
     # self.args is never stale; it's only updated from this file
@@ -399,6 +406,7 @@
 cdef class Metadata:
 
   def __cinit__(self, metadata):
+    grpc_init()
     self.metadata = list(metadata)
     for metadatum in metadata:
       if not isinstance(metadatum, Metadatum):
@@ -420,6 +428,7 @@
     # it'd be nice if that were documented somewhere...)
     # TODO(atash): document this in the C core
     grpc_metadata_array_destroy(&self.c_metadata_array)
+    grpc_shutdown()
 
   def __len__(self):
     return self.c_metadata_array.count
@@ -437,6 +446,7 @@
 cdef class Operation:
 
   def __cinit__(self):
+    grpc_init()
     self.references = []
     self._received_status_details = NULL
     self._received_status_details_capacity = 0
@@ -529,6 +539,7 @@
     # This means that we need to clean up after receive_status_on_client.
     if self.c_op.type == GRPC_OP_RECV_STATUS_ON_CLIENT:
       gpr_free(self._received_status_details)
+    grpc_shutdown()
 
 def operation_send_initial_metadata(Metadata metadata, int flags):
   cdef Operation op = Operation()
@@ -645,6 +656,7 @@
 cdef class Operations:
 
   def __cinit__(self, operations):
+    grpc_init()
     self.operations = list(operations)  # normalize iterable
     self.c_ops = NULL
     self.c_nops = 0
@@ -667,6 +679,7 @@
   def __dealloc__(self):
     with nogil:
       gpr_free(self.c_ops)
+    grpc_shutdown()
 
   def __iter__(self):
     return _OperationsIterator(self)
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi
index 23cee7b..20fc1c5 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/security.pyx.pxi
@@ -35,7 +35,7 @@
 cdef grpc_ssl_roots_override_result ssl_roots_override_callback(
     char **pem_root_certs) with gil:
   temporary_pem_root_certs = pkg_resources.resource_string(
-      'grpc._cython', '_credentials/roots.pem')
+      __name__.rstrip('.cygrpc'), '_credentials/roots.pem')
   pem_root_certs[0] = <char *>gpr_malloc(len(temporary_pem_root_certs) + 1)
   memcpy(
       pem_root_certs[0], <char *>temporary_pem_root_certs,
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
index 4f2d51b..18db38b 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx.pxi
@@ -34,11 +34,12 @@
 
 cdef class Server:
 
-  def __cinit__(self, ChannelArgs arguments=None):
+  def __cinit__(self, ChannelArgs arguments):
+    grpc_init()
     cdef grpc_channel_args *c_arguments = NULL
     self.references = []
     self.registered_completion_queues = []
-    if arguments is not None:
+    if len(arguments) > 0:
       c_arguments = &arguments.c_args
       self.references.append(arguments)
     with nogil:
@@ -172,3 +173,4 @@
         while not self.is_shutdown:
           time.sleep(0)
       grpc_server_destroy(self.c_server)
+    grpc_shutdown()
diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pxd b/src/python/grpcio/grpc/_cython/cygrpc.pxd
index 9779534..2645463 100644
--- a/src/python/grpcio/grpc/_cython/cygrpc.pxd
+++ b/src/python/grpcio/grpc/_cython/cygrpc.pxd
@@ -27,12 +27,12 @@
 # (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/_cython/_cygrpc/grpc.pxi"
+include "_cygrpc/grpc.pxi"
 
-include "grpc/_cython/_cygrpc/call.pxd.pxi"
-include "grpc/_cython/_cygrpc/channel.pxd.pxi"
-include "grpc/_cython/_cygrpc/credentials.pxd.pxi"
-include "grpc/_cython/_cygrpc/completion_queue.pxd.pxi"
-include "grpc/_cython/_cygrpc/records.pxd.pxi"
-include "grpc/_cython/_cygrpc/security.pxd.pxi"
-include "grpc/_cython/_cygrpc/server.pxd.pxi"
+include "_cygrpc/call.pxd.pxi"
+include "_cygrpc/channel.pxd.pxi"
+include "_cygrpc/credentials.pxd.pxi"
+include "_cygrpc/completion_queue.pxd.pxi"
+include "_cygrpc/records.pxd.pxi"
+include "_cygrpc/security.pxd.pxi"
+include "_cygrpc/server.pxd.pxi"
diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pyx b/src/python/grpcio/grpc/_cython/cygrpc.pyx
index a9520b9..e1bd046 100644
--- a/src/python/grpcio/grpc/_cython/cygrpc.pyx
+++ b/src/python/grpcio/grpc/_cython/cygrpc.pyx
@@ -35,14 +35,14 @@
 
 # TODO(atash): figure out why the coverage tool gets confused about the Cython
 # coverage plugin when the following files don't have a '.pxi' suffix.
-include "grpc/_cython/_cygrpc/grpc_string.pyx.pxi"
-include "grpc/_cython/_cygrpc/call.pyx.pxi"
-include "grpc/_cython/_cygrpc/channel.pyx.pxi"
-include "grpc/_cython/_cygrpc/credentials.pyx.pxi"
-include "grpc/_cython/_cygrpc/completion_queue.pyx.pxi"
-include "grpc/_cython/_cygrpc/records.pyx.pxi"
-include "grpc/_cython/_cygrpc/security.pyx.pxi"
-include "grpc/_cython/_cygrpc/server.pyx.pxi"
+include "_cygrpc/grpc_string.pyx.pxi"
+include "_cygrpc/call.pyx.pxi"
+include "_cygrpc/channel.pyx.pxi"
+include "_cygrpc/credentials.pyx.pxi"
+include "_cygrpc/completion_queue.pyx.pxi"
+include "_cygrpc/records.pyx.pxi"
+include "_cygrpc/security.pyx.pxi"
+include "_cygrpc/server.pyx.pxi"
 
 #
 # initialize gRPC
@@ -55,12 +55,8 @@
 
 
 def _initialize():
-  grpc_init()
   grpc_set_ssl_roots_override_callback(
           <grpc_ssl_roots_override_callback>ssl_roots_override_callback)
 
-  if Py_AtExit(grpc_shutdown) != 0:
-    raise ImportError('failed to register gRPC library shutdown callbacks')
-
 
 _initialize()
diff --git a/src/python/grpcio/grpc/_server.py b/src/python/grpcio/grpc/_server.py
index 94a13bf..5f846ce 100644
--- a/src/python/grpcio/grpc/_server.py
+++ b/src/python/grpcio/grpc/_server.py
@@ -728,12 +728,11 @@
         cleanup_server, target=_serve, args=(state,))
     thread.start()
 
-
 class Server(grpc.Server):
 
-  def __init__(self, thread_pool, generic_handlers):
+  def __init__(self, thread_pool, generic_handlers, options):
     completion_queue = cygrpc.CompletionQueue()
-    server = cygrpc.Server()
+    server = cygrpc.Server(_common.channel_args(options))
     server.register_completion_queue(completion_queue)
     self._state = _ServerState(
         completion_queue, server, generic_handlers, thread_pool)
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index fc0908b..2bebf48 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -50,7 +50,9 @@
   'src/core/lib/support/log_linux.c',
   'src/core/lib/support/log_posix.c',
   'src/core/lib/support/log_windows.c',
+  'src/core/lib/support/mpscq.c',
   'src/core/lib/support/murmur_hash.c',
+  'src/core/lib/support/percent_encoding.c',
   'src/core/lib/support/slice.c',
   'src/core/lib/support/slice_buffer.c',
   'src/core/lib/support/stack_lockfree.c',
@@ -81,9 +83,11 @@
   'src/core/lib/channel/channel_stack_builder.c',
   'src/core/lib/channel/compress_filter.c',
   'src/core/lib/channel/connected_channel.c',
+  'src/core/lib/channel/deadline_filter.c',
   'src/core/lib/channel/handshaker.c',
   'src/core/lib/channel/http_client_filter.c',
   'src/core/lib/channel/http_server_filter.c',
+  'src/core/lib/channel/message_size_filter.c',
   'src/core/lib/compression/compression.c',
   'src/core/lib/compression/message_compress.c',
   'src/core/lib/debug/trace.c',
@@ -91,6 +95,7 @@
   'src/core/lib/http/httpcli.c',
   'src/core/lib/http/parser.c',
   'src/core/lib/iomgr/closure.c',
+  'src/core/lib/iomgr/combiner.c',
   'src/core/lib/iomgr/endpoint.c',
   'src/core/lib/iomgr/endpoint_pair_posix.c',
   'src/core/lib/iomgr/endpoint_pair_windows.c',
@@ -162,6 +167,7 @@
   'src/core/lib/transport/metadata.c',
   'src/core/lib/transport/metadata_batch.c',
   'src/core/lib/transport/static_metadata.c',
+  'src/core/lib/transport/timeout_encoding.c',
   'src/core/lib/transport/transport.c',
   'src/core/lib/transport/transport_op_string.c',
   'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c',
@@ -184,7 +190,6 @@
   'src/core/ext/transport/chttp2/transport/status_conversion.c',
   'src/core/ext/transport/chttp2/transport/stream_lists.c',
   'src/core/ext/transport/chttp2/transport/stream_map.c',
-  'src/core/ext/transport/chttp2/transport/timeout_encoding.c',
   'src/core/ext/transport/chttp2/transport/varint.c',
   'src/core/ext/transport/chttp2/transport/writing.c',
   'src/core/ext/transport/chttp2/alpn/alpn.c',
@@ -220,10 +225,10 @@
   'src/core/ext/client_config/channel_connectivity.c',
   'src/core/ext/client_config/client_channel.c',
   'src/core/ext/client_config/client_channel_factory.c',
-  'src/core/ext/client_config/client_config.c',
   'src/core/ext/client_config/client_config_plugin.c',
   'src/core/ext/client_config/connector.c',
   'src/core/ext/client_config/default_initial_connect_string.c',
+  'src/core/ext/client_config/http_connect_handshaker.c',
   'src/core/ext/client_config/initial_connect_string.c',
   'src/core/ext/client_config/lb_policy.c',
   'src/core/ext/client_config/lb_policy_factory.c',
@@ -232,14 +237,15 @@
   'src/core/ext/client_config/resolver.c',
   'src/core/ext/client_config/resolver_factory.c',
   'src/core/ext/client_config/resolver_registry.c',
+  'src/core/ext/client_config/resolver_result.c',
   'src/core/ext/client_config/subchannel.c',
-  'src/core/ext/client_config/subchannel_call_holder.c',
   'src/core/ext/client_config/subchannel_index.c',
   'src/core/ext/client_config/uri_parser.c',
   'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c',
   'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c',
   'src/core/ext/transport/chttp2/client/insecure/channel_create.c',
   'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c',
+  'src/core/ext/lb_policy/grpclb/grpclb.c',
   'src/core/ext/lb_policy/grpclb/load_balancer_api.c',
   'src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c',
   'third_party/nanopb/pb_common.c',
@@ -251,8 +257,10 @@
   'src/core/ext/resolver/sockaddr/sockaddr_resolver.c',
   'src/core/ext/load_reporting/load_reporting.c',
   'src/core/ext/load_reporting/load_reporting_filter.c',
+  'src/core/ext/census/base_resources.c',
   'src/core/ext/census/context.c',
   'src/core/ext/census/gen/census.pb.c',
+  'src/core/ext/census/gen/trace_context.pb.c',
   'src/core/ext/census/grpc_context.c',
   'src/core/ext/census/grpc_filter.c',
   'src/core/ext/census/grpc_plugin.c',
@@ -260,6 +268,8 @@
   'src/core/ext/census/mlog.c',
   'src/core/ext/census/operation.c',
   'src/core/ext/census/placeholders.c',
+  'src/core/ext/census/resource.c',
+  'src/core/ext/census/trace_context.c',
   'src/core/ext/census/tracing.c',
   'src/core/plugin_registry/grpc_plugin_registry.c',
   'src/boringssl/err_data.c',
diff --git a/src/python/grpcio_health_checking/setup.py b/src/python/grpcio_health_checking/setup.py
index 727d628..8c92ee1 100644
--- a/src/python/grpcio_health_checking/setup.py
+++ b/src/python/grpcio_health_checking/setup.py
@@ -46,11 +46,12 @@
 }
 
 SETUP_REQUIRES = (
-    'grpcio-tools>=0.15.0',
+    'grpcio-tools>={version}'.format(version=grpc_version.VERSION),
 )
 
 INSTALL_REQUIRES = (
-    'grpcio>=0.15.0',
+    'protobuf>=3.0.0',
+    'grpcio>={version}'.format(version=grpc_version.VERSION),
 )
 
 COMMAND_CLASS = {
diff --git a/src/python/grpcio_tests/setup.py b/src/python/grpcio_tests/setup.py
index 0afaf7d..7384206 100644
--- a/src/python/grpcio_tests/setup.py
+++ b/src/python/grpcio_tests/setup.py
@@ -31,13 +31,9 @@
 
 import os
 import os.path
-import shutil
 import sys
 
-from distutils import core as _core
-from distutils import extension as _extension
 import setuptools
-from setuptools.command import egg_info
 
 import grpc.tools.command
 
@@ -60,17 +56,14 @@
     'coverage>=4.0',
     'enum34>=1.0.4',
     'futures>=2.2.0',
-    'grpcio>=0.14.0',
-    'grpcio-health-checking>=0.14.0',
+    'grpcio>={version}'.format(version=grpc_version.VERSION),
+    'grpcio-tools>={version}'.format(version=grpc_version.VERSION),
+    'grpcio-health-checking>={version}'.format(version=grpc_version.VERSION),
     'oauth2client>=1.4.7',
-    'protobuf>=3.0.0a3',
+    'protobuf>=3.0.0',
     'six>=1.10',
 )
 
-SETUP_REQUIRES = (
-    'grpcio-tools>=0.14.0',
-)
-
 COMMAND_CLASS = {
     # Run `preprocess` *before* doing any packaging!
     'preprocess': commands.GatherProto,
@@ -115,7 +108,6 @@
   package_dir=PACKAGE_DIRECTORIES,
   package_data=PACKAGE_DATA,
   install_requires=INSTALL_REQUIRES,
-  setup_requires=SETUP_REQUIRES,
   cmdclass=COMMAND_CLASS,
   tests_require=TESTS_REQUIRE,
   test_suite=TEST_SUITE,
diff --git a/src/python/grpcio_tests/tests/interop/_insecure_interop_test.py b/src/python/grpcio_tests/tests/interop/_insecure_interop_test.py
index c753d6f..936c895 100644
--- a/src/python/grpcio_tests/tests/interop/_insecure_interop_test.py
+++ b/src/python/grpcio_tests/tests/interop/_insecure_interop_test.py
@@ -29,9 +29,10 @@
 
 """Insecure client-server interoperability as a unit test."""
 
+from concurrent import futures
 import unittest
 
-from grpc.beta import implementations
+import grpc
 from src.proto.grpc.testing import test_pb2
 
 from tests.interop import _interop_test_case
@@ -44,14 +45,13 @@
     unittest.TestCase):
 
   def setUp(self):
-    self.server = test_pb2.beta_create_TestService_server(methods.TestService())
+    self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
+    test_pb2.add_TestServiceServicer_to_server(
+        methods.TestService(), self.server)
     port = self.server.add_insecure_port('[::]:0')
     self.server.start()
-    self.stub = test_pb2.beta_create_TestService_stub(
-        implementations.insecure_channel('localhost', port))
-
-  def tearDown(self):
-    self.server.stop(0)
+    self.stub = test_pb2.TestServiceStub(
+        grpc.insecure_channel('localhost:{}'.format(port)))
 
 
 if __name__ == '__main__':
diff --git a/src/python/grpcio_tests/tests/interop/_secure_interop_test.py b/src/python/grpcio_tests/tests/interop/_secure_interop_test.py
index cb09f54..eaca553 100644
--- a/src/python/grpcio_tests/tests/interop/_secure_interop_test.py
+++ b/src/python/grpcio_tests/tests/interop/_secure_interop_test.py
@@ -29,17 +29,16 @@
 
 """Secure client-server interoperability as a unit test."""
 
+from concurrent import futures
 import unittest
 
-from grpc.beta import implementations
+import grpc
 from src.proto.grpc.testing import test_pb2
 
 from tests.interop import _interop_test_case
 from tests.interop import methods
 from tests.interop import resources
 
-from tests.unit.beta import test_utilities
-
 _SERVER_HOST_OVERRIDE = 'foo.test.google.fr'
 
 
@@ -48,19 +47,18 @@
     unittest.TestCase):
 
   def setUp(self):
-    self.server = test_pb2.beta_create_TestService_server(methods.TestService())
+    self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
+    test_pb2.add_TestServiceServicer_to_server(
+        methods.TestService(), self.server)
     port = self.server.add_secure_port(
-        '[::]:0', implementations.ssl_server_credentials(
+        '[::]:0', grpc.ssl_server_credentials(
             [(resources.private_key(), resources.certificate_chain())]))
     self.server.start()
-    self.stub = test_pb2.beta_create_TestService_stub(
-        test_utilities.not_really_secure_channel(
-            'localhost', port, implementations.ssl_channel_credentials(
-                resources.test_root_certificates()),
-                _SERVER_HOST_OVERRIDE))
-
-  def tearDown(self):
-    self.server.stop(0)
+    self.stub = test_pb2.TestServiceStub(
+        grpc.secure_channel(
+            'localhost:{}'.format(port),
+            grpc.ssl_channel_credentials(resources.test_root_certificates()),
+            (('grpc.ssl_target_name_override', _SERVER_HOST_OVERRIDE,),)))
 
 
 if __name__ == '__main__':
diff --git a/src/python/grpcio_tests/tests/interop/client.py b/src/python/grpcio_tests/tests/interop/client.py
index 8aa1ce3..9d61d18 100644
--- a/src/python/grpcio_tests/tests/interop/client.py
+++ b/src/python/grpcio_tests/tests/interop/client.py
@@ -32,14 +32,12 @@
 import argparse
 from oauth2client import client as oauth2client_client
 
+import grpc
 from grpc.beta import implementations
 from src.proto.grpc.testing import test_pb2
 
 from tests.interop import methods
 from tests.interop import resources
-from tests.unit.beta import test_utilities
-
-_ONE_DAY_IN_SECONDS = 60 * 60 * 24
 
 
 def _args():
@@ -66,41 +64,49 @@
   return parser.parse_args()
 
 
+def _application_default_credentials():
+  return oauth2client_client.GoogleCredentials.get_application_default()
+
+
 def _stub(args):
+  target = '{}:{}'.format(args.server_host, args.server_port)
   if args.test_case == 'oauth2_auth_token':
-    creds = oauth2client_client.GoogleCredentials.get_application_default()
-    scoped_creds = creds.create_scoped([args.oauth_scope])
-    access_token = scoped_creds.get_access_token().access_token
-    call_creds = implementations.access_token_call_credentials(access_token)
+    google_credentials = _application_default_credentials()
+    scoped_credentials = google_credentials.create_scoped([args.oauth_scope])
+    access_token = scoped_credentials.get_access_token().access_token
+    call_credentials = grpc.access_token_call_credentials(access_token)
   elif args.test_case == 'compute_engine_creds':
-    creds = oauth2client_client.GoogleCredentials.get_application_default()
-    scoped_creds = creds.create_scoped([args.oauth_scope])
-    call_creds = implementations.google_call_credentials(scoped_creds)
+    google_credentials = _application_default_credentials()
+    scoped_credentials = google_credentials.create_scoped([args.oauth_scope])
+    # TODO(https://github.com/grpc/grpc/issues/6799): Eliminate this last
+    # remaining use of the Beta API.
+    call_credentials = implementations.google_call_credentials(
+        scoped_credentials)
   elif args.test_case == 'jwt_token_creds':
-    creds = oauth2client_client.GoogleCredentials.get_application_default()
-    call_creds = implementations.google_call_credentials(creds)
+    google_credentials = _application_default_credentials()
+    # TODO(https://github.com/grpc/grpc/issues/6799): Eliminate this last
+    # remaining use of the Beta API.
+    call_credentials = implementations.google_call_credentials(
+        google_credentials)
   else:
-    call_creds = None
+    call_credentials = None
   if args.use_tls:
     if args.use_test_ca:
       root_certificates = resources.test_root_certificates()
     else:
       root_certificates = None  # will load default roots.
 
-    channel_creds = implementations.ssl_channel_credentials(root_certificates)
-    if call_creds is not None:
-      channel_creds = implementations.composite_channel_credentials(
-          channel_creds, call_creds)
+    channel_credentials = grpc.ssl_channel_credentials(root_certificates)
+    if call_credentials is not None:
+      channel_credentials = grpc.composite_channel_credentials(
+          channel_credentials, call_credentials)
 
-    channel = test_utilities.not_really_secure_channel(
-        args.server_host, args.server_port, channel_creds,
-        args.server_host_override)
-    stub = test_pb2.beta_create_TestService_stub(channel)
+    channel = grpc.secure_channel(
+        target, channel_credentials,
+        (('grpc.ssl_target_name_override', args.server_host_override,),))
   else:
-    channel = implementations.insecure_channel(
-        args.server_host, args.server_port)
-    stub = test_pb2.beta_create_TestService_stub(channel)
-  return stub
+    channel = grpc.insecure_channel(target)
+  return test_pb2.TestServiceStub(channel)
 
 
 def _test_case_from_arg(test_case_arg):
diff --git a/src/python/grpcio_tests/tests/interop/methods.py b/src/python/grpcio_tests/tests/interop/methods.py
index 97e6c9e..7edd75c 100644
--- a/src/python/grpcio_tests/tests/interop/methods.py
+++ b/src/python/grpcio_tests/tests/interop/methods.py
@@ -29,8 +29,6 @@
 
 """Implementations of interoperability test methods."""
 
-from __future__ import print_function
-
 import enum
 import json
 import os
@@ -41,26 +39,21 @@
 
 import grpc
 from grpc.beta import implementations
-from grpc.beta import interfaces
-from grpc.framework.common import cardinality
-from grpc.framework.interfaces.face import face
 
 from src.proto.grpc.testing import empty_pb2
 from src.proto.grpc.testing import messages_pb2
 from src.proto.grpc.testing import test_pb2
 
-_TIMEOUT = 7
 
-
-class TestService(test_pb2.BetaTestServiceServicer):
+class TestService(test_pb2.TestServiceServicer):
 
   def EmptyCall(self, request, context):
     return empty_pb2.Empty()
 
   def UnaryCall(self, request, context):
     if request.HasField('response_status'):
-      context.code(request.response_status.code)
-      context.details(request.response_status.message)
+      context.set_code(request.response_status.code)
+      context.set_details(request.response_status.message)
     return messages_pb2.SimpleResponse(
         payload=messages_pb2.Payload(
             type=messages_pb2.COMPRESSABLE,
@@ -68,8 +61,8 @@
 
   def StreamingOutputCall(self, request, context):
     if request.HasField('response_status'):
-      context.code(request.response_status.code)
-      context.details(request.response_status.message)
+      context.set_code(request.response_status.code)
+      context.set_details(request.response_status.message)
     for response_parameters in request.response_parameters:
       yield messages_pb2.StreamingOutputCallResponse(
           payload=messages_pb2.Payload(
@@ -79,7 +72,7 @@
   def StreamingInputCall(self, request_iterator, context):
     aggregate_size = 0
     for request in request_iterator:
-      if request.payload and request.payload.body:
+      if request.payload is not None and request.payload.body:
         aggregate_size += len(request.payload.body)
     return messages_pb2.StreamingInputCallResponse(
         aggregated_payload_size=aggregate_size)
@@ -87,8 +80,8 @@
   def FullDuplexCall(self, request_iterator, context):
     for request in request_iterator:
       if request.HasField('response_status'):
-        context.code(request.response_status.code)
-        context.details(request.response_status.message)
+        context.set_code(request.response_status.code)
+        context.set_details(request.response_status.message)
       for response_parameters in request.response_parameters:
         yield messages_pb2.StreamingOutputCallResponse(
             payload=messages_pb2.Payload(
@@ -101,83 +94,80 @@
     return self.FullDuplexCall(request_iterator, context)
 
 
-def _large_unary_common_behavior(stub, fill_username, fill_oauth_scope,
-                                 protocol_options=None):
-  with stub:
-    request = messages_pb2.SimpleRequest(
-        response_type=messages_pb2.COMPRESSABLE, response_size=314159,
-        payload=messages_pb2.Payload(body=b'\x00' * 271828),
-        fill_username=fill_username, fill_oauth_scope=fill_oauth_scope)
-    response_future = stub.UnaryCall.future(request, _TIMEOUT,
-                                            protocol_options=protocol_options)
-    response = response_future.result()
-    if response.payload.type is not messages_pb2.COMPRESSABLE:
-      raise ValueError(
-          'response payload type is "%s"!' % type(response.payload.type))
-    if len(response.payload.body) != 314159:
-      raise ValueError(
-          'response body of incorrect size %d!' % len(response.payload.body))
+def _large_unary_common_behavior(
+    stub, fill_username, fill_oauth_scope, call_credentials):
+  request = messages_pb2.SimpleRequest(
+      response_type=messages_pb2.COMPRESSABLE, response_size=314159,
+      payload=messages_pb2.Payload(body=b'\x00' * 271828),
+      fill_username=fill_username, fill_oauth_scope=fill_oauth_scope)
+  response_future = stub.UnaryCall.future(
+      request, credentials=call_credentials)
+  response = response_future.result()
+  if response.payload.type is not messages_pb2.COMPRESSABLE:
+    raise ValueError(
+        'response payload type is "%s"!' % type(response.payload.type))
+  elif len(response.payload.body) != 314159:
+    raise ValueError(
+        'response body of incorrect size %d!' % len(response.payload.body))
+  else:
     return response
 
 
 def _empty_unary(stub):
-  with stub:
-    response = stub.EmptyCall(empty_pb2.Empty(), _TIMEOUT)
-    if not isinstance(response, empty_pb2.Empty):
-      raise TypeError(
-          'response is of type "%s", not empty_pb2.Empty!', type(response))
+  response = stub.EmptyCall(empty_pb2.Empty())
+  if not isinstance(response, empty_pb2.Empty):
+    raise TypeError(
+        'response is of type "%s", not empty_pb2.Empty!', type(response))
 
 
 def _large_unary(stub):
-  _large_unary_common_behavior(stub, False, False)
+  _large_unary_common_behavior(stub, False, False, None)
 
 
 def _client_streaming(stub):
-  with stub:
-    payload_body_sizes = (27182, 8, 1828, 45904)
-    payloads = (
-        messages_pb2.Payload(body=b'\x00' * size)
-        for size in payload_body_sizes)
-    requests = (
-        messages_pb2.StreamingInputCallRequest(payload=payload)
-        for payload in payloads)
-    response = stub.StreamingInputCall(requests, _TIMEOUT)
-    if response.aggregated_payload_size != 74922:
-      raise ValueError(
-          'incorrect size %d!' % response.aggregated_payload_size)
+  payload_body_sizes = (27182, 8, 1828, 45904,)
+  payloads = (
+      messages_pb2.Payload(body=b'\x00' * size)
+      for size in payload_body_sizes)
+  requests = (
+      messages_pb2.StreamingInputCallRequest(payload=payload)
+      for payload in payloads)
+  response = stub.StreamingInputCall(requests)
+  if response.aggregated_payload_size != 74922:
+    raise ValueError(
+        'incorrect size %d!' % response.aggregated_payload_size)
 
 
 def _server_streaming(stub):
-  sizes = (31415, 9, 2653, 58979)
+  sizes = (31415, 9, 2653, 58979,)
 
-  with stub:
-    request = messages_pb2.StreamingOutputCallRequest(
-        response_type=messages_pb2.COMPRESSABLE,
-        response_parameters=(
-            messages_pb2.ResponseParameters(size=sizes[0]),
-            messages_pb2.ResponseParameters(size=sizes[1]),
-            messages_pb2.ResponseParameters(size=sizes[2]),
-            messages_pb2.ResponseParameters(size=sizes[3]),
-        ))
-    response_iterator = stub.StreamingOutputCall(request, _TIMEOUT)
-    for index, response in enumerate(response_iterator):
-      if response.payload.type != messages_pb2.COMPRESSABLE:
-        raise ValueError(
-            'response body of invalid type %s!' % response.payload.type)
-      if len(response.payload.body) != sizes[index]:
-        raise ValueError(
-            'response body of invalid size %d!' % len(response.payload.body))
+  request = messages_pb2.StreamingOutputCallRequest(
+      response_type=messages_pb2.COMPRESSABLE,
+      response_parameters=(
+          messages_pb2.ResponseParameters(size=sizes[0]),
+          messages_pb2.ResponseParameters(size=sizes[1]),
+          messages_pb2.ResponseParameters(size=sizes[2]),
+          messages_pb2.ResponseParameters(size=sizes[3]),
+      )
+  )
+  response_iterator = stub.StreamingOutputCall(request)
+  for index, response in enumerate(response_iterator):
+    if response.payload.type != messages_pb2.COMPRESSABLE:
+      raise ValueError(
+          'response body of invalid type %s!' % response.payload.type)
+    elif len(response.payload.body) != sizes[index]:
+      raise ValueError(
+          'response body of invalid size %d!' % len(response.payload.body))
 
 def _cancel_after_begin(stub):
-  with stub:
-    sizes = (27182, 8, 1828, 45904)
-    payloads = [messages_pb2.Payload(body=b'\x00' * size) for size in sizes]
-    requests = [messages_pb2.StreamingInputCallRequest(payload=payload)
-                for payload in payloads]
-    responses = stub.StreamingInputCall.future(requests, _TIMEOUT)
-    responses.cancel()
-    if not responses.cancelled():
-      raise ValueError('expected call to be cancelled')
+  sizes = (27182, 8, 1828, 45904,)
+  payloads = (messages_pb2.Payload(body=b'\x00' * size) for size in sizes)
+  requests = (messages_pb2.StreamingInputCallRequest(payload=payload)
+              for payload in payloads)
+  response_future = stub.StreamingInputCall.future(requests)
+  response_future.cancel()
+  if not response_future.cancelled():
+    raise ValueError('expected call to be cancelled')
 
 
 class _Pipe(object):
@@ -220,18 +210,17 @@
 
 
 def _ping_pong(stub):
-  request_response_sizes = (31415, 9, 2653, 58979)
-  request_payload_sizes = (27182, 8, 1828, 45904)
+  request_response_sizes = (31415, 9, 2653, 58979,)
+  request_payload_sizes = (27182, 8, 1828, 45904,)
 
-  with stub, _Pipe() as pipe:
-    response_iterator = stub.FullDuplexCall(pipe, _TIMEOUT)
-    print('Starting ping-pong with response iterator %s' % response_iterator)
+  with _Pipe() as pipe:
+    response_iterator = stub.FullDuplexCall(pipe)
     for response_size, payload_size in zip(
         request_response_sizes, request_payload_sizes):
       request = messages_pb2.StreamingOutputCallRequest(
           response_type=messages_pb2.COMPRESSABLE,
-          response_parameters=(messages_pb2.ResponseParameters(
-              size=response_size),),
+          response_parameters=(
+              messages_pb2.ResponseParameters(size=response_size),),
           payload=messages_pb2.Payload(body=b'\x00' * payload_size))
       pipe.add(request)
       response = next(response_iterator)
@@ -244,17 +233,17 @@
 
 
 def _cancel_after_first_response(stub):
-  request_response_sizes = (31415, 9, 2653, 58979)
-  request_payload_sizes = (27182, 8, 1828, 45904)
-  with stub, _Pipe() as pipe:
-    response_iterator = stub.FullDuplexCall(pipe, _TIMEOUT)
+  request_response_sizes = (31415, 9, 2653, 58979,)
+  request_payload_sizes = (27182, 8, 1828, 45904,)
+  with _Pipe() as pipe:
+    response_iterator = stub.FullDuplexCall(pipe)
 
     response_size = request_response_sizes[0]
     payload_size = request_payload_sizes[0]
     request = messages_pb2.StreamingOutputCallRequest(
         response_type=messages_pb2.COMPRESSABLE,
-        response_parameters=(messages_pb2.ResponseParameters(
-            size=response_size),),
+        response_parameters=(
+            messages_pb2.ResponseParameters(size=response_size),),
         payload=messages_pb2.Payload(body=b'\x00' * payload_size))
     pipe.add(request)
     response = next(response_iterator)
@@ -264,16 +253,17 @@
 
     try:
       next(response_iterator)
-    except Exception:
-      pass
+    except grpc.RpcError as rpc_error:
+      if rpc_error.code() is not grpc.StatusCode.CANCELLED:
+        raise
     else:
       raise ValueError('expected call to be cancelled')
 
 
 def _timeout_on_sleeping_server(stub):
   request_payload_size = 27182
-  with stub, _Pipe() as pipe:
-    response_iterator = stub.FullDuplexCall(pipe, 0.001)
+  with _Pipe() as pipe:
+    response_iterator = stub.FullDuplexCall(pipe, timeout=0.001)
 
     request = messages_pb2.StreamingOutputCallRequest(
         response_type=messages_pb2.COMPRESSABLE,
@@ -282,15 +272,16 @@
     time.sleep(0.1)
     try:
       next(response_iterator)
-    except face.ExpirationError:
-      pass
+    except grpc.RpcError as rpc_error:
+      if rpc_error.code() is not grpc.StatusCode.DEADLINE_EXCEEDED:
+        raise
     else:
       raise ValueError('expected call to exceed deadline')
 
 
 def _empty_stream(stub):
-  with stub, _Pipe() as pipe:
-    response_iterator = stub.FullDuplexCall(pipe, _TIMEOUT)
+  with _Pipe() as pipe:
+    response_iterator = stub.FullDuplexCall(pipe)
     pipe.close()
     try:
       next(response_iterator)
@@ -300,65 +291,64 @@
 
 
 def _status_code_and_message(stub):
-  with stub:
-    message = 'test status message'
-    code = 2
-    status = grpc.StatusCode.UNKNOWN # code = 2
-    request = messages_pb2.SimpleRequest(
-        response_type=messages_pb2.COMPRESSABLE,
-        response_size=1,
-        payload=messages_pb2.Payload(body=b'\x00'),
-        response_status=messages_pb2.EchoStatus(code=code, message=message)
-        )
-    response_future = stub.UnaryCall.future(request, _TIMEOUT)
-    if response_future.code() != status:
-      raise ValueError(
-        'expected code %s, got %s' % (status, response_future.code()))
-    if response_future.details() != message:
-      raise ValueError(
-        'expected message %s, got %s' % (message, response_future.details()))
+  message = 'test status message'
+  code = 2
+  status = grpc.StatusCode.UNKNOWN # code = 2
+  request = messages_pb2.SimpleRequest(
+      response_type=messages_pb2.COMPRESSABLE,
+      response_size=1,
+      payload=messages_pb2.Payload(body=b'\x00'),
+      response_status=messages_pb2.EchoStatus(code=code, message=message)
+  )
+  response_future = stub.UnaryCall.future(request)
+  if response_future.code() != status:
+    raise ValueError(
+      'expected code %s, got %s' % (status, response_future.code()))
+  elif response_future.details() != message:
+    raise ValueError(
+      'expected message %s, got %s' % (message, response_future.details()))
 
-    request = messages_pb2.StreamingOutputCallRequest(
-        response_type=messages_pb2.COMPRESSABLE,
-        response_parameters=(
-            messages_pb2.ResponseParameters(size=1),),
-        response_status=messages_pb2.EchoStatus(code=code, message=message))
-    response_iterator = stub.StreamingOutputCall(request, _TIMEOUT)
-    if response_future.code() != status:
-      raise ValueError(
-        'expected code %s, got %s' % (status, response_iterator.code()))
-    if response_future.details() != message:
-      raise ValueError(
-        'expected message %s, got %s' % (message, response_iterator.details()))
+  request = messages_pb2.StreamingOutputCallRequest(
+      response_type=messages_pb2.COMPRESSABLE,
+      response_parameters=(
+          messages_pb2.ResponseParameters(size=1),),
+      response_status=messages_pb2.EchoStatus(code=code, message=message))
+  response_iterator = stub.StreamingOutputCall(request)
+  if response_future.code() != status:
+    raise ValueError(
+      'expected code %s, got %s' % (status, response_iterator.code()))
+  elif response_future.details() != message:
+    raise ValueError(
+      'expected message %s, got %s' % (message, response_iterator.details()))
 
 
 def _compute_engine_creds(stub, args):
-  response = _large_unary_common_behavior(stub, True, True)
+  response = _large_unary_common_behavior(stub, True, True, None)
   if args.default_service_account != response.username:
     raise ValueError(
-        'expected username %s, got %s' % (args.default_service_account,
-                                          response.username))
+        'expected username %s, got %s' % (
+            args.default_service_account, response.username))
 
 
 def _oauth2_auth_token(stub, args):
   json_key_filename = os.environ[
       oauth2client_client.GOOGLE_APPLICATION_CREDENTIALS]
   wanted_email = json.load(open(json_key_filename, 'rb'))['client_email']
-  response = _large_unary_common_behavior(stub, True, True)
+  response = _large_unary_common_behavior(stub, True, True, None)
   if wanted_email != response.username:
     raise ValueError(
         'expected username %s, got %s' % (wanted_email, response.username))
   if args.oauth_scope.find(response.oauth_scope) == -1:
     raise ValueError(
-        'expected to find oauth scope "%s" in received "%s"' %
-        (response.oauth_scope, args.oauth_scope))
+        'expected to find oauth scope "{}" in received "{}"'.format(
+            response.oauth_scope, args.oauth_scope))
 
 
 def _jwt_token_creds(stub, args):
   json_key_filename = os.environ[
       oauth2client_client.GOOGLE_APPLICATION_CREDENTIALS]
   wanted_email = json.load(open(json_key_filename, 'rb'))['client_email']
-  response = _large_unary_common_behavior(stub, True, False)
+  response = _large_unary_common_behavior(stub, True, False, None)
   if wanted_email != response.username:
     raise ValueError(
         'expected username %s, got %s' % (wanted_email, response.username))
@@ -370,11 +360,11 @@
   wanted_email = json.load(open(json_key_filename, 'rb'))['client_email']
   credentials = oauth2client_client.GoogleCredentials.get_application_default()
   scoped_credentials = credentials.create_scoped([args.oauth_scope])
-  call_creds = implementations.google_call_credentials(scoped_credentials)
-  options = interfaces.grpc_call_options(disable_compression=False,
-                                         credentials=call_creds)
-  response = _large_unary_common_behavior(stub, True, False,
-                                          protocol_options=options)
+  # TODO(https://github.com/grpc/grpc/issues/6799): Eliminate this last
+  # remaining use of the Beta API.
+  call_credentials = implementations.google_call_credentials(
+      scoped_credentials)
+  response = _large_unary_common_behavior(stub, True, False, call_credentials)
   if wanted_email != response.username:
     raise ValueError(
         'expected username %s, got %s' % (wanted_email, response.username))
diff --git a/src/python/grpcio_tests/tests/interop/server.py b/src/python/grpcio_tests/tests/interop/server.py
index ab2c3c7..1ae83bc 100644
--- a/src/python/grpcio_tests/tests/interop/server.py
+++ b/src/python/grpcio_tests/tests/interop/server.py
@@ -30,10 +30,11 @@
 """The Python implementation of the GRPC interoperability test server."""
 
 import argparse
+from concurrent import futures
 import logging
 import time
 
-from grpc.beta import implementations
+import grpc
 from src.proto.grpc.testing import test_pb2
 
 from tests.interop import methods
@@ -51,12 +52,13 @@
       default=False, type=resources.parse_bool)
   args = parser.parse_args()
 
-  server = test_pb2.beta_create_TestService_server(methods.TestService())
+  server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
+  test_pb2.add_TestServiceServicer_to_server(methods.TestService(), server)
   if args.use_tls:
     private_key = resources.private_key()
     certificate_chain = resources.certificate_chain()
-    credentials = implementations.ssl_server_credentials(
-        [(private_key, certificate_chain)])
+    credentials = grpc.ssl_server_credentials(
+        ((private_key, certificate_chain),))
     server.add_secure_port('[::]:{}'.format(args.port), credentials)
   else:
     server.add_insecure_port('[::]:{}'.format(args.port))
@@ -68,7 +70,7 @@
       time.sleep(_ONE_DAY_IN_SECONDS)
   except BaseException as e:
     logging.info('Caught exception "%s"; stopping server...', e)
-    server.stop(0)
+    server.stop(None)
     logging.info('Server stopped; exiting.')
 
 if __name__ == '__main__':
diff --git a/src/python/grpcio_tests/tests/stress/client.py b/src/python/grpcio_tests/tests/stress/client.py
index 0de2532..975f33b 100644
--- a/src/python/grpcio_tests/tests/stress/client.py
+++ b/src/python/grpcio_tests/tests/stress/client.py
@@ -30,9 +30,10 @@
 """Entry point for running stress tests."""
 
 import argparse
+from concurrent import futures
 import threading
 
-from grpc.beta import implementations
+import grpc
 from six.moves import queue
 from src.proto.grpc.testing import metrics_pb2
 from src.proto.grpc.testing import test_pb2
@@ -92,24 +93,24 @@
 
 def run_test(args):
   test_cases = _parse_weighted_test_cases(args.test_cases)
-  test_servers = args.server_addresses.split(',')
+  test_server_targets = args.server_addresses.split(',')
   # Propagate any client exceptions with a queue
   exception_queue = queue.Queue()
   stop_event = threading.Event()
   hist = histogram.Histogram(1, 1)
   runners = []
 
-  server = metrics_pb2.beta_create_MetricsService_server(
-      metrics_server.MetricsServer(hist))
+  server = grpc.server(futures.ThreadPoolExecutor(max_workers=25))
+  metrics_pb2.add_MetricsServiceServicer_to_server(
+      metrics_server.MetricsServer(hist), server)
   server.add_insecure_port('[::]:{}'.format(args.metrics_port))
   server.start()
 
-  for test_server in test_servers:
-    host, port = test_server.split(':', 1)
+  for test_server_target in test_server_targets:
     for _ in xrange(args.num_channels_per_server):
-      channel = implementations.insecure_channel(host, int(port))
+      channel = grpc.insecure_channel(test_server_target)
       for _ in xrange(args.num_stubs_per_channel):
-        stub = test_pb2.beta_create_TestService_stub(channel)
+        stub = test_pb2.TestServiceStub(channel)
         runner = test_runner.TestRunner(stub, test_cases, hist,
                                         exception_queue, stop_event)
         runners.append(runner)
@@ -128,8 +129,8 @@
     stop_event.set()
     for runner in runners:
       runner.join()
-      runner = None
-    server.stop(0)
+    runner = None
+    server.stop(None)
 
 if __name__ == '__main__':
   run_test(_args())
diff --git a/src/python/grpcio_tests/tests/stress/metrics_server.py b/src/python/grpcio_tests/tests/stress/metrics_server.py
index b994e46..33dd1d6 100644
--- a/src/python/grpcio_tests/tests/stress/metrics_server.py
+++ b/src/python/grpcio_tests/tests/stress/metrics_server.py
@@ -36,7 +36,7 @@
 GAUGE_NAME = 'python_overall_qps'
 
 
-class MetricsServer(metrics_pb2.BetaMetricsServiceServicer):
+class MetricsServer(metrics_pb2.MetricsServiceServicer):
 
   def __init__(self, histogram):
     self._start_time = time.time()
diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json
index dcaef0d..2071a33 100644
--- a/src/python/grpcio_tests/tests/tests.json
+++ b/src/python/grpcio_tests/tests/tests.json
@@ -7,6 +7,7 @@
   "_beta_features_test.BetaFeaturesTest", 
   "_beta_features_test.ContextManagementAndLifecycleTest", 
   "_cancel_many_calls_test.CancelManyCallsTest",
+  "_channel_args_test.ChannelArgsTest",
   "_channel_connectivity_test.ChannelConnectivityTest",
   "_channel_ready_future_test.ChannelReadyFutureTest",
   "_channel_test.ChannelTest", 
diff --git a/src/python/grpcio_tests/tests/unit/_channel_args_test.py b/src/python/grpcio_tests/tests/unit/_channel_args_test.py
new file mode 100644
index 0000000..6a636d7
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_channel_args_test.py
@@ -0,0 +1,53 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Tests of Channel Args on client/server side."""
+
+import unittest
+
+import grpc
+
+TEST_CHANNEL_ARGS = (
+    ('arg1', b'bytes_val'),
+    ('arg2', 'str_val'),
+    ('arg3', 1),
+    (b'arg4', 'str_val'),
+)
+
+
+class ChannelArgsTest(unittest.TestCase):
+
+  def test_client(self):
+    grpc.insecure_channel('localhost:8080', options=TEST_CHANNEL_ARGS)
+
+  def test_server(self):
+    grpc.server(None, options=TEST_CHANNEL_ARGS)
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py b/src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py
index 3c00f68..1324ad3 100644
--- a/src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py
+++ b/src/python/grpcio_tests/tests/unit/_channel_connectivity_test.py
@@ -32,12 +32,12 @@
 import threading
 import time
 import unittest
-from concurrent import futures
 
 import grpc
 from grpc import _channel
 from grpc import _server
 from tests.unit.framework.common import test_constants
+from tests.unit import _thread_pool
 
 
 def _ready_in_connectivities(connectivities):
@@ -78,7 +78,7 @@
   def test_lonely_channel_connectivity(self):
     callback = _Callback()
 
-    channel = _channel.Channel('localhost:12345', None, None)
+    channel = _channel.Channel('localhost:12345', (), None)
     channel.subscribe(callback.update, try_to_connect=False)
     first_connectivities = callback.block_until_connectivities_satisfy(bool)
     channel.subscribe(callback.update, try_to_connect=True)
@@ -104,13 +104,14 @@
         grpc.ChannelConnectivity.READY, fifth_connectivities)
 
   def test_immediately_connectable_channel_connectivity(self):
-    server = _server.Server(futures.ThreadPoolExecutor(max_workers=0), ())
+    thread_pool = _thread_pool.RecordingThreadPool(max_workers=None)
+    server = _server.Server(thread_pool, (), ())
     port = server.add_insecure_port('[::]:0')
     server.start()
     first_callback = _Callback()
     second_callback = _Callback()
 
-    channel = _channel.Channel('localhost:{}'.format(port), None, None)
+    channel = _channel.Channel('localhost:{}'.format(port), (), None)
     channel.subscribe(first_callback.update, try_to_connect=False)
     first_connectivities = first_callback.block_until_connectivities_satisfy(
         bool)
@@ -141,20 +142,23 @@
         fourth_connectivities)
     self.assertNotIn(
         grpc.ChannelConnectivity.SHUTDOWN, fourth_connectivities)
+    self.assertFalse(thread_pool.was_used())
 
   def test_reachable_then_unreachable_channel_connectivity(self):
-    server = _server.Server(futures.ThreadPoolExecutor(max_workers=0), ())
+    thread_pool = _thread_pool.RecordingThreadPool(max_workers=None)
+    server = _server.Server(thread_pool, (), ())
     port = server.add_insecure_port('[::]:0')
     server.start()
     callback = _Callback()
 
-    channel = _channel.Channel('localhost:{}'.format(port), None, None)
+    channel = _channel.Channel('localhost:{}'.format(port), (), None)
     channel.subscribe(callback.update, try_to_connect=True)
     callback.block_until_connectivities_satisfy(_ready_in_connectivities)
     # Now take down the server and confirm that channel readiness is repudiated.
     server.stop(None)
     callback.block_until_connectivities_satisfy(_last_connectivity_is_not_ready)
     channel.unsubscribe(callback.update)
+    self.assertFalse(thread_pool.was_used())
 
 
 if __name__ == '__main__':
diff --git a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
index e8982ed..60d478b 100644
--- a/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
+++ b/src/python/grpcio_tests/tests/unit/_channel_ready_future_test.py
@@ -31,12 +31,12 @@
 
 import threading
 import unittest
-from concurrent import futures
 
 import grpc
 from grpc import _channel
 from grpc import _server
 from tests.unit.framework.common import test_constants
+from tests.unit import _thread_pool
 
 
 class _Callback(object):
@@ -78,7 +78,8 @@
     self.assertFalse(ready_future.running())
 
   def test_immediately_connectable_channel_connectivity(self):
-    server = _server.Server(futures.ThreadPoolExecutor(max_workers=0), ())
+    thread_pool = _thread_pool.RecordingThreadPool(max_workers=None)
+    server = _server.Server(thread_pool, (), ())
     port = server.add_insecure_port('[::]:0')
     server.start()
     channel = grpc.insecure_channel('localhost:{}'.format(port))
@@ -97,6 +98,7 @@
     self.assertFalse(ready_future.cancelled())
     self.assertTrue(ready_future.done())
     self.assertFalse(ready_future.running())
+    self.assertFalse(thread_pool.was_used())
 
 
 if __name__ == '__main__':
diff --git a/src/python/grpcio_tests/tests/unit/_cython/_cancel_many_calls_test.py b/src/python/grpcio_tests/tests/unit/_cython/_cancel_many_calls_test.py
index cf212c5..20115fb 100644
--- a/src/python/grpcio_tests/tests/unit/_cython/_cancel_many_calls_test.py
+++ b/src/python/grpcio_tests/tests/unit/_cython/_cancel_many_calls_test.py
@@ -157,11 +157,12 @@
     server_thread_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY)
 
     server_completion_queue = cygrpc.CompletionQueue()
-    server = cygrpc.Server()
+    server = cygrpc.Server(cygrpc.ChannelArgs([]))
     server.register_completion_queue(server_completion_queue)
     port = server.add_http2_port(b'[::]:0')
     server.start()
-    channel = cygrpc.Channel('localhost:{}'.format(port).encode())
+    channel = cygrpc.Channel('localhost:{}'.format(port).encode(),
+                             cygrpc.ChannelArgs([]))
 
     state = _State()
 
diff --git a/src/python/grpcio_tests/tests/unit/_cython/_read_some_but_not_all_responses_test.py b/src/python/grpcio_tests/tests/unit/_cython/_read_some_but_not_all_responses_test.py
index 152d8edde..2ae5285 100644
--- a/src/python/grpcio_tests/tests/unit/_cython/_read_some_but_not_all_responses_test.py
+++ b/src/python/grpcio_tests/tests/unit/_cython/_read_some_but_not_all_responses_test.py
@@ -124,11 +124,12 @@
 
   def testReadSomeButNotAllResponses(self):
     server_completion_queue = cygrpc.CompletionQueue()
-    server = cygrpc.Server()
+    server = cygrpc.Server(cygrpc.ChannelArgs([]))
     server.register_completion_queue(server_completion_queue)
     port = server.add_http2_port(b'[::]:0')
     server.start()
-    channel = cygrpc.Channel('localhost:{}'.format(port).encode())
+    channel = cygrpc.Channel('localhost:{}'.format(port).encode(),
+                             cygrpc.ChannelArgs([]))
 
     server_shutdown_tag = 'server_shutdown_tag'
     server_driver = _ServerDriver(server_completion_queue, server_shutdown_tag)
diff --git a/src/python/grpcio_tests/tests/unit/_cython/cygrpc_test.py b/src/python/grpcio_tests/tests/unit/_cython/cygrpc_test.py
index f9a8e24..8dedebf 100644
--- a/src/python/grpcio_tests/tests/unit/_cython/cygrpc_test.py
+++ b/src/python/grpcio_tests/tests/unit/_cython/cygrpc_test.py
@@ -30,6 +30,7 @@
 import time
 import threading
 import unittest
+import platform
 
 from grpc._cython import cygrpc
 from tests.unit._cython import test_utilities
@@ -120,7 +121,7 @@
     del call_credentials
 
   def testServerStartNoExplicitShutdown(self):
-    server = cygrpc.Server()
+    server = cygrpc.Server(cygrpc.ChannelArgs([]))
     completion_queue = cygrpc.CompletionQueue()
     server.register_completion_queue(completion_queue)
     port = server.add_http2_port(b'[::]:0')
@@ -130,7 +131,7 @@
 
   def testServerStartShutdown(self):
     completion_queue = cygrpc.CompletionQueue()
-    server = cygrpc.Server()
+    server = cygrpc.Server(cygrpc.ChannelArgs([]))
     server.add_http2_port(b'[::]:0')
     server.register_completion_queue(completion_queue)
     server.start()
@@ -147,7 +148,7 @@
 
   def setUpMixin(self, server_credentials, client_credentials, host_override):
     self.server_completion_queue = cygrpc.CompletionQueue()
-    self.server = cygrpc.Server()
+    self.server = cygrpc.Server(cygrpc.ChannelArgs([]))
     self.server.register_completion_queue(self.server_completion_queue)
     if server_credentials:
       self.port = self.server.add_http2_port(b'[::]:0', server_credentials)
@@ -163,7 +164,8 @@
           'localhost:{}'.format(self.port).encode(), client_channel_arguments,
           client_credentials)
     else:
-      self.client_channel = cygrpc.Channel('localhost:{}'.format(self.port).encode())
+      self.client_channel = cygrpc.Channel(
+          'localhost:{}'.format(self.port).encode(), cygrpc.ChannelArgs([]))
     if host_override:
       self.host_argument = None  # default host
       self.expected_host = host_override
diff --git a/src/python/grpcio_tests/tests/unit/_thread_pool.py b/src/python/grpcio_tests/tests/unit/_thread_pool.py
new file mode 100644
index 0000000..f13cc2f
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_thread_pool.py
@@ -0,0 +1,48 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import threading
+from concurrent import futures
+
+
+class RecordingThreadPool(futures.Executor):
+  """A thread pool that records if used."""
+  def __init__(self, max_workers):
+    self._tp_executor = futures.ThreadPoolExecutor(max_workers=max_workers)
+    self._lock = threading.Lock()
+    self._was_used = False
+
+  def submit(self, fn, *args, **kwargs):
+    with self._lock:
+      self._was_used = True
+    self._tp_executor.submit(fn, *args, **kwargs)
+
+  def was_used(self):
+    with self._lock:
+      return self._was_used
diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb
index 6d65db8..66c54aa 100644
--- a/src/ruby/ext/grpc/extconf.rb
+++ b/src/ruby/ext/grpc/extconf.rb
@@ -91,6 +91,10 @@
   $LDFLAGS << ' -fprofile-arcs -ftest-coverage -rdynamic'
 end
 
+if grpc_config == 'dbg'
+  $CFLAGS << ' -O0'
+end
+
 $LDFLAGS << ' -Wl,-wrap,memcpy' if RUBY_PLATFORM =~ /linux/
 $LDFLAGS << ' -static' if windows
 
diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c
index e6d30a1..3b2b88e 100644
--- a/src/ruby/ext/grpc/rb_channel.c
+++ b/src/ruby/ext/grpc/rb_channel.c
@@ -386,7 +386,7 @@
   rb_define_const(grpc_rb_cChannel, "MAX_CONCURRENT_STREAMS",
                   ID2SYM(rb_intern(GRPC_ARG_MAX_CONCURRENT_STREAMS)));
   rb_define_const(grpc_rb_cChannel, "MAX_MESSAGE_LENGTH",
-                  ID2SYM(rb_intern(GRPC_ARG_MAX_MESSAGE_LENGTH)));
+                  ID2SYM(rb_intern(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH)));
   id_insecure_channel = rb_intern("this_channel_is_insecure");
   Init_grpc_propagate_masks();
   Init_grpc_connectivity_states();
diff --git a/src/ruby/ext/grpc/rb_compression_options.c b/src/ruby/ext/grpc/rb_compression_options.c
index 0a3a215..c5668fd 100644
--- a/src/ruby/ext/grpc/rb_compression_options.c
+++ b/src/ruby/ext/grpc/rb_compression_options.c
@@ -38,7 +38,7 @@
 
 #include <grpc/compression.h>
 #include <grpc/grpc.h>
-#include <grpc/impl/codegen/alloc.h>
+#include <grpc/support/alloc.h>
 #include <grpc/impl/codegen/compression_types.h>
 #include <grpc/impl/codegen/grpc_types.h>
 #include <string.h>
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index d7f862c..a6cad0d 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -37,6 +37,16 @@
 
 #include "rb_grpc_imports.generated.h"
 
+grpc_raw_byte_buffer_create_type grpc_raw_byte_buffer_create_import;
+grpc_raw_compressed_byte_buffer_create_type grpc_raw_compressed_byte_buffer_create_import;
+grpc_byte_buffer_copy_type grpc_byte_buffer_copy_import;
+grpc_byte_buffer_length_type grpc_byte_buffer_length_import;
+grpc_byte_buffer_destroy_type grpc_byte_buffer_destroy_import;
+grpc_byte_buffer_reader_init_type grpc_byte_buffer_reader_init_import;
+grpc_byte_buffer_reader_destroy_type grpc_byte_buffer_reader_destroy_import;
+grpc_byte_buffer_reader_next_type grpc_byte_buffer_reader_next_import;
+grpc_byte_buffer_reader_readall_type grpc_byte_buffer_reader_readall_import;
+grpc_raw_byte_buffer_from_reader_type grpc_raw_byte_buffer_from_reader_import;
 census_initialize_type census_initialize_import;
 census_shutdown_type census_shutdown_import;
 census_supported_type census_supported_import;
@@ -61,15 +71,10 @@
 census_trace_scan_start_type census_trace_scan_start_import;
 census_get_trace_record_type census_get_trace_record_import;
 census_trace_scan_end_type census_trace_scan_end_import;
+census_define_resource_type census_define_resource_import;
+census_delete_resource_type census_delete_resource_import;
+census_resource_id_type census_resource_id_import;
 census_record_values_type census_record_values_import;
-census_view_create_type census_view_create_import;
-census_view_delete_type census_view_delete_import;
-census_view_metric_type census_view_metric_import;
-census_view_naggregations_type census_view_naggregations_import;
-census_view_tags_type census_view_tags_import;
-census_view_aggregrations_type census_view_aggregrations_import;
-census_view_get_data_type census_view_get_data_import;
-census_view_reset_type census_view_reset_import;
 grpc_compression_algorithm_parse_type grpc_compression_algorithm_parse_import;
 grpc_compression_algorithm_name_type grpc_compression_algorithm_name_import;
 grpc_compression_algorithm_for_level_type grpc_compression_algorithm_for_level_import;
@@ -85,6 +90,7 @@
 grpc_init_type grpc_init_import;
 grpc_shutdown_type grpc_shutdown_import;
 grpc_version_string_type grpc_version_string_import;
+grpc_g_stands_for_type grpc_g_stands_for_import;
 grpc_completion_queue_create_type grpc_completion_queue_create_import;
 grpc_completion_queue_next_type grpc_completion_queue_next_import;
 grpc_completion_queue_pluck_type grpc_completion_queue_pluck_import;
@@ -168,94 +174,6 @@
 gpr_free_aligned_type gpr_free_aligned_import;
 gpr_set_allocation_functions_type gpr_set_allocation_functions_import;
 gpr_get_allocation_functions_type gpr_get_allocation_functions_import;
-grpc_raw_byte_buffer_create_type grpc_raw_byte_buffer_create_import;
-grpc_raw_compressed_byte_buffer_create_type grpc_raw_compressed_byte_buffer_create_import;
-grpc_byte_buffer_copy_type grpc_byte_buffer_copy_import;
-grpc_byte_buffer_length_type grpc_byte_buffer_length_import;
-grpc_byte_buffer_destroy_type grpc_byte_buffer_destroy_import;
-grpc_byte_buffer_reader_init_type grpc_byte_buffer_reader_init_import;
-grpc_byte_buffer_reader_destroy_type grpc_byte_buffer_reader_destroy_import;
-grpc_byte_buffer_reader_next_type grpc_byte_buffer_reader_next_import;
-grpc_byte_buffer_reader_readall_type grpc_byte_buffer_reader_readall_import;
-grpc_raw_byte_buffer_from_reader_type grpc_raw_byte_buffer_from_reader_import;
-gpr_log_type gpr_log_import;
-gpr_log_message_type gpr_log_message_import;
-gpr_set_log_verbosity_type gpr_set_log_verbosity_import;
-gpr_log_verbosity_init_type gpr_log_verbosity_init_import;
-gpr_set_log_function_type gpr_set_log_function_import;
-gpr_slice_ref_type gpr_slice_ref_import;
-gpr_slice_unref_type gpr_slice_unref_import;
-gpr_slice_new_type gpr_slice_new_import;
-gpr_slice_new_with_user_data_type gpr_slice_new_with_user_data_import;
-gpr_slice_new_with_len_type gpr_slice_new_with_len_import;
-gpr_slice_malloc_type gpr_slice_malloc_import;
-gpr_slice_from_copied_string_type gpr_slice_from_copied_string_import;
-gpr_slice_from_copied_buffer_type gpr_slice_from_copied_buffer_import;
-gpr_slice_from_static_string_type gpr_slice_from_static_string_import;
-gpr_slice_sub_type gpr_slice_sub_import;
-gpr_slice_sub_no_ref_type gpr_slice_sub_no_ref_import;
-gpr_slice_split_tail_type gpr_slice_split_tail_import;
-gpr_slice_split_head_type gpr_slice_split_head_import;
-gpr_empty_slice_type gpr_empty_slice_import;
-gpr_slice_cmp_type gpr_slice_cmp_import;
-gpr_slice_str_cmp_type gpr_slice_str_cmp_import;
-gpr_slice_buffer_init_type gpr_slice_buffer_init_import;
-gpr_slice_buffer_destroy_type gpr_slice_buffer_destroy_import;
-gpr_slice_buffer_add_type gpr_slice_buffer_add_import;
-gpr_slice_buffer_add_indexed_type gpr_slice_buffer_add_indexed_import;
-gpr_slice_buffer_addn_type gpr_slice_buffer_addn_import;
-gpr_slice_buffer_tiny_add_type gpr_slice_buffer_tiny_add_import;
-gpr_slice_buffer_pop_type gpr_slice_buffer_pop_import;
-gpr_slice_buffer_reset_and_unref_type gpr_slice_buffer_reset_and_unref_import;
-gpr_slice_buffer_swap_type gpr_slice_buffer_swap_import;
-gpr_slice_buffer_move_into_type gpr_slice_buffer_move_into_import;
-gpr_slice_buffer_trim_end_type gpr_slice_buffer_trim_end_import;
-gpr_slice_buffer_move_first_type gpr_slice_buffer_move_first_import;
-gpr_slice_buffer_take_first_type gpr_slice_buffer_take_first_import;
-gpr_mu_init_type gpr_mu_init_import;
-gpr_mu_destroy_type gpr_mu_destroy_import;
-gpr_mu_lock_type gpr_mu_lock_import;
-gpr_mu_unlock_type gpr_mu_unlock_import;
-gpr_mu_trylock_type gpr_mu_trylock_import;
-gpr_cv_init_type gpr_cv_init_import;
-gpr_cv_destroy_type gpr_cv_destroy_import;
-gpr_cv_wait_type gpr_cv_wait_import;
-gpr_cv_signal_type gpr_cv_signal_import;
-gpr_cv_broadcast_type gpr_cv_broadcast_import;
-gpr_once_init_type gpr_once_init_import;
-gpr_event_init_type gpr_event_init_import;
-gpr_event_set_type gpr_event_set_import;
-gpr_event_get_type gpr_event_get_import;
-gpr_event_wait_type gpr_event_wait_import;
-gpr_ref_init_type gpr_ref_init_import;
-gpr_ref_type gpr_ref_import;
-gpr_ref_non_zero_type gpr_ref_non_zero_import;
-gpr_refn_type gpr_refn_import;
-gpr_unref_type gpr_unref_import;
-gpr_stats_init_type gpr_stats_init_import;
-gpr_stats_inc_type gpr_stats_inc_import;
-gpr_stats_read_type gpr_stats_read_import;
-gpr_time_0_type gpr_time_0_import;
-gpr_inf_future_type gpr_inf_future_import;
-gpr_inf_past_type gpr_inf_past_import;
-gpr_time_init_type gpr_time_init_import;
-gpr_now_type gpr_now_import;
-gpr_convert_clock_type_type gpr_convert_clock_type_import;
-gpr_time_cmp_type gpr_time_cmp_import;
-gpr_time_max_type gpr_time_max_import;
-gpr_time_min_type gpr_time_min_import;
-gpr_time_add_type gpr_time_add_import;
-gpr_time_sub_type gpr_time_sub_import;
-gpr_time_from_micros_type gpr_time_from_micros_import;
-gpr_time_from_nanos_type gpr_time_from_nanos_import;
-gpr_time_from_millis_type gpr_time_from_millis_import;
-gpr_time_from_seconds_type gpr_time_from_seconds_import;
-gpr_time_from_minutes_type gpr_time_from_minutes_import;
-gpr_time_from_hours_type gpr_time_from_hours_import;
-gpr_time_to_millis_type gpr_time_to_millis_import;
-gpr_time_similar_type gpr_time_similar_import;
-gpr_sleep_until_type gpr_sleep_until_import;
-gpr_timespec_to_micros_type gpr_timespec_to_micros_import;
 gpr_avl_create_type gpr_avl_create_import;
 gpr_avl_ref_type gpr_avl_ref_import;
 gpr_avl_unref_type gpr_avl_unref_import;
@@ -292,7 +210,41 @@
 gpr_histogram_merge_contents_type gpr_histogram_merge_contents_import;
 gpr_join_host_port_type gpr_join_host_port_import;
 gpr_split_host_port_type gpr_split_host_port_import;
+gpr_log_type gpr_log_import;
+gpr_log_message_type gpr_log_message_import;
+gpr_set_log_verbosity_type gpr_set_log_verbosity_import;
+gpr_log_verbosity_init_type gpr_log_verbosity_init_import;
+gpr_set_log_function_type gpr_set_log_function_import;
 gpr_format_message_type gpr_format_message_import;
+gpr_slice_ref_type gpr_slice_ref_import;
+gpr_slice_unref_type gpr_slice_unref_import;
+gpr_slice_new_type gpr_slice_new_import;
+gpr_slice_new_with_user_data_type gpr_slice_new_with_user_data_import;
+gpr_slice_new_with_len_type gpr_slice_new_with_len_import;
+gpr_slice_malloc_type gpr_slice_malloc_import;
+gpr_slice_from_copied_string_type gpr_slice_from_copied_string_import;
+gpr_slice_from_copied_buffer_type gpr_slice_from_copied_buffer_import;
+gpr_slice_from_static_string_type gpr_slice_from_static_string_import;
+gpr_slice_sub_type gpr_slice_sub_import;
+gpr_slice_sub_no_ref_type gpr_slice_sub_no_ref_import;
+gpr_slice_split_tail_type gpr_slice_split_tail_import;
+gpr_slice_split_head_type gpr_slice_split_head_import;
+gpr_empty_slice_type gpr_empty_slice_import;
+gpr_slice_cmp_type gpr_slice_cmp_import;
+gpr_slice_str_cmp_type gpr_slice_str_cmp_import;
+gpr_slice_buffer_init_type gpr_slice_buffer_init_import;
+gpr_slice_buffer_destroy_type gpr_slice_buffer_destroy_import;
+gpr_slice_buffer_add_type gpr_slice_buffer_add_import;
+gpr_slice_buffer_add_indexed_type gpr_slice_buffer_add_indexed_import;
+gpr_slice_buffer_addn_type gpr_slice_buffer_addn_import;
+gpr_slice_buffer_tiny_add_type gpr_slice_buffer_tiny_add_import;
+gpr_slice_buffer_pop_type gpr_slice_buffer_pop_import;
+gpr_slice_buffer_reset_and_unref_type gpr_slice_buffer_reset_and_unref_import;
+gpr_slice_buffer_swap_type gpr_slice_buffer_swap_import;
+gpr_slice_buffer_move_into_type gpr_slice_buffer_move_into_import;
+gpr_slice_buffer_trim_end_type gpr_slice_buffer_trim_end_import;
+gpr_slice_buffer_move_first_type gpr_slice_buffer_move_first_import;
+gpr_slice_buffer_take_first_type gpr_slice_buffer_take_first_import;
 gpr_strdup_type gpr_strdup_import;
 gpr_asprintf_type gpr_asprintf_import;
 gpr_subprocess_binary_extension_type gpr_subprocess_binary_extension_import;
@@ -300,6 +252,29 @@
 gpr_subprocess_destroy_type gpr_subprocess_destroy_import;
 gpr_subprocess_join_type gpr_subprocess_join_import;
 gpr_subprocess_interrupt_type gpr_subprocess_interrupt_import;
+gpr_mu_init_type gpr_mu_init_import;
+gpr_mu_destroy_type gpr_mu_destroy_import;
+gpr_mu_lock_type gpr_mu_lock_import;
+gpr_mu_unlock_type gpr_mu_unlock_import;
+gpr_mu_trylock_type gpr_mu_trylock_import;
+gpr_cv_init_type gpr_cv_init_import;
+gpr_cv_destroy_type gpr_cv_destroy_import;
+gpr_cv_wait_type gpr_cv_wait_import;
+gpr_cv_signal_type gpr_cv_signal_import;
+gpr_cv_broadcast_type gpr_cv_broadcast_import;
+gpr_once_init_type gpr_once_init_import;
+gpr_event_init_type gpr_event_init_import;
+gpr_event_set_type gpr_event_set_import;
+gpr_event_get_type gpr_event_get_import;
+gpr_event_wait_type gpr_event_wait_import;
+gpr_ref_init_type gpr_ref_init_import;
+gpr_ref_type gpr_ref_import;
+gpr_ref_non_zero_type gpr_ref_non_zero_import;
+gpr_refn_type gpr_refn_import;
+gpr_unref_type gpr_unref_import;
+gpr_stats_init_type gpr_stats_init_import;
+gpr_stats_inc_type gpr_stats_inc_import;
+gpr_stats_read_type gpr_stats_read_import;
 gpr_thd_new_type gpr_thd_new_import;
 gpr_thd_options_default_type gpr_thd_options_default_import;
 gpr_thd_options_set_detached_type gpr_thd_options_set_detached_import;
@@ -308,8 +283,39 @@
 gpr_thd_options_is_joinable_type gpr_thd_options_is_joinable_import;
 gpr_thd_currentid_type gpr_thd_currentid_import;
 gpr_thd_join_type gpr_thd_join_import;
+gpr_time_0_type gpr_time_0_import;
+gpr_inf_future_type gpr_inf_future_import;
+gpr_inf_past_type gpr_inf_past_import;
+gpr_time_init_type gpr_time_init_import;
+gpr_now_type gpr_now_import;
+gpr_convert_clock_type_type gpr_convert_clock_type_import;
+gpr_time_cmp_type gpr_time_cmp_import;
+gpr_time_max_type gpr_time_max_import;
+gpr_time_min_type gpr_time_min_import;
+gpr_time_add_type gpr_time_add_import;
+gpr_time_sub_type gpr_time_sub_import;
+gpr_time_from_micros_type gpr_time_from_micros_import;
+gpr_time_from_nanos_type gpr_time_from_nanos_import;
+gpr_time_from_millis_type gpr_time_from_millis_import;
+gpr_time_from_seconds_type gpr_time_from_seconds_import;
+gpr_time_from_minutes_type gpr_time_from_minutes_import;
+gpr_time_from_hours_type gpr_time_from_hours_import;
+gpr_time_to_millis_type gpr_time_to_millis_import;
+gpr_time_similar_type gpr_time_similar_import;
+gpr_sleep_until_type gpr_sleep_until_import;
+gpr_timespec_to_micros_type gpr_timespec_to_micros_import;
 
 void grpc_rb_load_imports(HMODULE library) {
+  grpc_raw_byte_buffer_create_import = (grpc_raw_byte_buffer_create_type) GetProcAddress(library, "grpc_raw_byte_buffer_create");
+  grpc_raw_compressed_byte_buffer_create_import = (grpc_raw_compressed_byte_buffer_create_type) GetProcAddress(library, "grpc_raw_compressed_byte_buffer_create");
+  grpc_byte_buffer_copy_import = (grpc_byte_buffer_copy_type) GetProcAddress(library, "grpc_byte_buffer_copy");
+  grpc_byte_buffer_length_import = (grpc_byte_buffer_length_type) GetProcAddress(library, "grpc_byte_buffer_length");
+  grpc_byte_buffer_destroy_import = (grpc_byte_buffer_destroy_type) GetProcAddress(library, "grpc_byte_buffer_destroy");
+  grpc_byte_buffer_reader_init_import = (grpc_byte_buffer_reader_init_type) GetProcAddress(library, "grpc_byte_buffer_reader_init");
+  grpc_byte_buffer_reader_destroy_import = (grpc_byte_buffer_reader_destroy_type) GetProcAddress(library, "grpc_byte_buffer_reader_destroy");
+  grpc_byte_buffer_reader_next_import = (grpc_byte_buffer_reader_next_type) GetProcAddress(library, "grpc_byte_buffer_reader_next");
+  grpc_byte_buffer_reader_readall_import = (grpc_byte_buffer_reader_readall_type) GetProcAddress(library, "grpc_byte_buffer_reader_readall");
+  grpc_raw_byte_buffer_from_reader_import = (grpc_raw_byte_buffer_from_reader_type) GetProcAddress(library, "grpc_raw_byte_buffer_from_reader");
   census_initialize_import = (census_initialize_type) GetProcAddress(library, "census_initialize");
   census_shutdown_import = (census_shutdown_type) GetProcAddress(library, "census_shutdown");
   census_supported_import = (census_supported_type) GetProcAddress(library, "census_supported");
@@ -334,15 +340,10 @@
   census_trace_scan_start_import = (census_trace_scan_start_type) GetProcAddress(library, "census_trace_scan_start");
   census_get_trace_record_import = (census_get_trace_record_type) GetProcAddress(library, "census_get_trace_record");
   census_trace_scan_end_import = (census_trace_scan_end_type) GetProcAddress(library, "census_trace_scan_end");
+  census_define_resource_import = (census_define_resource_type) GetProcAddress(library, "census_define_resource");
+  census_delete_resource_import = (census_delete_resource_type) GetProcAddress(library, "census_delete_resource");
+  census_resource_id_import = (census_resource_id_type) GetProcAddress(library, "census_resource_id");
   census_record_values_import = (census_record_values_type) GetProcAddress(library, "census_record_values");
-  census_view_create_import = (census_view_create_type) GetProcAddress(library, "census_view_create");
-  census_view_delete_import = (census_view_delete_type) GetProcAddress(library, "census_view_delete");
-  census_view_metric_import = (census_view_metric_type) GetProcAddress(library, "census_view_metric");
-  census_view_naggregations_import = (census_view_naggregations_type) GetProcAddress(library, "census_view_naggregations");
-  census_view_tags_import = (census_view_tags_type) GetProcAddress(library, "census_view_tags");
-  census_view_aggregrations_import = (census_view_aggregrations_type) GetProcAddress(library, "census_view_aggregrations");
-  census_view_get_data_import = (census_view_get_data_type) GetProcAddress(library, "census_view_get_data");
-  census_view_reset_import = (census_view_reset_type) GetProcAddress(library, "census_view_reset");
   grpc_compression_algorithm_parse_import = (grpc_compression_algorithm_parse_type) GetProcAddress(library, "grpc_compression_algorithm_parse");
   grpc_compression_algorithm_name_import = (grpc_compression_algorithm_name_type) GetProcAddress(library, "grpc_compression_algorithm_name");
   grpc_compression_algorithm_for_level_import = (grpc_compression_algorithm_for_level_type) GetProcAddress(library, "grpc_compression_algorithm_for_level");
@@ -358,6 +359,7 @@
   grpc_init_import = (grpc_init_type) GetProcAddress(library, "grpc_init");
   grpc_shutdown_import = (grpc_shutdown_type) GetProcAddress(library, "grpc_shutdown");
   grpc_version_string_import = (grpc_version_string_type) GetProcAddress(library, "grpc_version_string");
+  grpc_g_stands_for_import = (grpc_g_stands_for_type) GetProcAddress(library, "grpc_g_stands_for");
   grpc_completion_queue_create_import = (grpc_completion_queue_create_type) GetProcAddress(library, "grpc_completion_queue_create");
   grpc_completion_queue_next_import = (grpc_completion_queue_next_type) GetProcAddress(library, "grpc_completion_queue_next");
   grpc_completion_queue_pluck_import = (grpc_completion_queue_pluck_type) GetProcAddress(library, "grpc_completion_queue_pluck");
@@ -441,94 +443,6 @@
   gpr_free_aligned_import = (gpr_free_aligned_type) GetProcAddress(library, "gpr_free_aligned");
   gpr_set_allocation_functions_import = (gpr_set_allocation_functions_type) GetProcAddress(library, "gpr_set_allocation_functions");
   gpr_get_allocation_functions_import = (gpr_get_allocation_functions_type) GetProcAddress(library, "gpr_get_allocation_functions");
-  grpc_raw_byte_buffer_create_import = (grpc_raw_byte_buffer_create_type) GetProcAddress(library, "grpc_raw_byte_buffer_create");
-  grpc_raw_compressed_byte_buffer_create_import = (grpc_raw_compressed_byte_buffer_create_type) GetProcAddress(library, "grpc_raw_compressed_byte_buffer_create");
-  grpc_byte_buffer_copy_import = (grpc_byte_buffer_copy_type) GetProcAddress(library, "grpc_byte_buffer_copy");
-  grpc_byte_buffer_length_import = (grpc_byte_buffer_length_type) GetProcAddress(library, "grpc_byte_buffer_length");
-  grpc_byte_buffer_destroy_import = (grpc_byte_buffer_destroy_type) GetProcAddress(library, "grpc_byte_buffer_destroy");
-  grpc_byte_buffer_reader_init_import = (grpc_byte_buffer_reader_init_type) GetProcAddress(library, "grpc_byte_buffer_reader_init");
-  grpc_byte_buffer_reader_destroy_import = (grpc_byte_buffer_reader_destroy_type) GetProcAddress(library, "grpc_byte_buffer_reader_destroy");
-  grpc_byte_buffer_reader_next_import = (grpc_byte_buffer_reader_next_type) GetProcAddress(library, "grpc_byte_buffer_reader_next");
-  grpc_byte_buffer_reader_readall_import = (grpc_byte_buffer_reader_readall_type) GetProcAddress(library, "grpc_byte_buffer_reader_readall");
-  grpc_raw_byte_buffer_from_reader_import = (grpc_raw_byte_buffer_from_reader_type) GetProcAddress(library, "grpc_raw_byte_buffer_from_reader");
-  gpr_log_import = (gpr_log_type) GetProcAddress(library, "gpr_log");
-  gpr_log_message_import = (gpr_log_message_type) GetProcAddress(library, "gpr_log_message");
-  gpr_set_log_verbosity_import = (gpr_set_log_verbosity_type) GetProcAddress(library, "gpr_set_log_verbosity");
-  gpr_log_verbosity_init_import = (gpr_log_verbosity_init_type) GetProcAddress(library, "gpr_log_verbosity_init");
-  gpr_set_log_function_import = (gpr_set_log_function_type) GetProcAddress(library, "gpr_set_log_function");
-  gpr_slice_ref_import = (gpr_slice_ref_type) GetProcAddress(library, "gpr_slice_ref");
-  gpr_slice_unref_import = (gpr_slice_unref_type) GetProcAddress(library, "gpr_slice_unref");
-  gpr_slice_new_import = (gpr_slice_new_type) GetProcAddress(library, "gpr_slice_new");
-  gpr_slice_new_with_user_data_import = (gpr_slice_new_with_user_data_type) GetProcAddress(library, "gpr_slice_new_with_user_data");
-  gpr_slice_new_with_len_import = (gpr_slice_new_with_len_type) GetProcAddress(library, "gpr_slice_new_with_len");
-  gpr_slice_malloc_import = (gpr_slice_malloc_type) GetProcAddress(library, "gpr_slice_malloc");
-  gpr_slice_from_copied_string_import = (gpr_slice_from_copied_string_type) GetProcAddress(library, "gpr_slice_from_copied_string");
-  gpr_slice_from_copied_buffer_import = (gpr_slice_from_copied_buffer_type) GetProcAddress(library, "gpr_slice_from_copied_buffer");
-  gpr_slice_from_static_string_import = (gpr_slice_from_static_string_type) GetProcAddress(library, "gpr_slice_from_static_string");
-  gpr_slice_sub_import = (gpr_slice_sub_type) GetProcAddress(library, "gpr_slice_sub");
-  gpr_slice_sub_no_ref_import = (gpr_slice_sub_no_ref_type) GetProcAddress(library, "gpr_slice_sub_no_ref");
-  gpr_slice_split_tail_import = (gpr_slice_split_tail_type) GetProcAddress(library, "gpr_slice_split_tail");
-  gpr_slice_split_head_import = (gpr_slice_split_head_type) GetProcAddress(library, "gpr_slice_split_head");
-  gpr_empty_slice_import = (gpr_empty_slice_type) GetProcAddress(library, "gpr_empty_slice");
-  gpr_slice_cmp_import = (gpr_slice_cmp_type) GetProcAddress(library, "gpr_slice_cmp");
-  gpr_slice_str_cmp_import = (gpr_slice_str_cmp_type) GetProcAddress(library, "gpr_slice_str_cmp");
-  gpr_slice_buffer_init_import = (gpr_slice_buffer_init_type) GetProcAddress(library, "gpr_slice_buffer_init");
-  gpr_slice_buffer_destroy_import = (gpr_slice_buffer_destroy_type) GetProcAddress(library, "gpr_slice_buffer_destroy");
-  gpr_slice_buffer_add_import = (gpr_slice_buffer_add_type) GetProcAddress(library, "gpr_slice_buffer_add");
-  gpr_slice_buffer_add_indexed_import = (gpr_slice_buffer_add_indexed_type) GetProcAddress(library, "gpr_slice_buffer_add_indexed");
-  gpr_slice_buffer_addn_import = (gpr_slice_buffer_addn_type) GetProcAddress(library, "gpr_slice_buffer_addn");
-  gpr_slice_buffer_tiny_add_import = (gpr_slice_buffer_tiny_add_type) GetProcAddress(library, "gpr_slice_buffer_tiny_add");
-  gpr_slice_buffer_pop_import = (gpr_slice_buffer_pop_type) GetProcAddress(library, "gpr_slice_buffer_pop");
-  gpr_slice_buffer_reset_and_unref_import = (gpr_slice_buffer_reset_and_unref_type) GetProcAddress(library, "gpr_slice_buffer_reset_and_unref");
-  gpr_slice_buffer_swap_import = (gpr_slice_buffer_swap_type) GetProcAddress(library, "gpr_slice_buffer_swap");
-  gpr_slice_buffer_move_into_import = (gpr_slice_buffer_move_into_type) GetProcAddress(library, "gpr_slice_buffer_move_into");
-  gpr_slice_buffer_trim_end_import = (gpr_slice_buffer_trim_end_type) GetProcAddress(library, "gpr_slice_buffer_trim_end");
-  gpr_slice_buffer_move_first_import = (gpr_slice_buffer_move_first_type) GetProcAddress(library, "gpr_slice_buffer_move_first");
-  gpr_slice_buffer_take_first_import = (gpr_slice_buffer_take_first_type) GetProcAddress(library, "gpr_slice_buffer_take_first");
-  gpr_mu_init_import = (gpr_mu_init_type) GetProcAddress(library, "gpr_mu_init");
-  gpr_mu_destroy_import = (gpr_mu_destroy_type) GetProcAddress(library, "gpr_mu_destroy");
-  gpr_mu_lock_import = (gpr_mu_lock_type) GetProcAddress(library, "gpr_mu_lock");
-  gpr_mu_unlock_import = (gpr_mu_unlock_type) GetProcAddress(library, "gpr_mu_unlock");
-  gpr_mu_trylock_import = (gpr_mu_trylock_type) GetProcAddress(library, "gpr_mu_trylock");
-  gpr_cv_init_import = (gpr_cv_init_type) GetProcAddress(library, "gpr_cv_init");
-  gpr_cv_destroy_import = (gpr_cv_destroy_type) GetProcAddress(library, "gpr_cv_destroy");
-  gpr_cv_wait_import = (gpr_cv_wait_type) GetProcAddress(library, "gpr_cv_wait");
-  gpr_cv_signal_import = (gpr_cv_signal_type) GetProcAddress(library, "gpr_cv_signal");
-  gpr_cv_broadcast_import = (gpr_cv_broadcast_type) GetProcAddress(library, "gpr_cv_broadcast");
-  gpr_once_init_import = (gpr_once_init_type) GetProcAddress(library, "gpr_once_init");
-  gpr_event_init_import = (gpr_event_init_type) GetProcAddress(library, "gpr_event_init");
-  gpr_event_set_import = (gpr_event_set_type) GetProcAddress(library, "gpr_event_set");
-  gpr_event_get_import = (gpr_event_get_type) GetProcAddress(library, "gpr_event_get");
-  gpr_event_wait_import = (gpr_event_wait_type) GetProcAddress(library, "gpr_event_wait");
-  gpr_ref_init_import = (gpr_ref_init_type) GetProcAddress(library, "gpr_ref_init");
-  gpr_ref_import = (gpr_ref_type) GetProcAddress(library, "gpr_ref");
-  gpr_ref_non_zero_import = (gpr_ref_non_zero_type) GetProcAddress(library, "gpr_ref_non_zero");
-  gpr_refn_import = (gpr_refn_type) GetProcAddress(library, "gpr_refn");
-  gpr_unref_import = (gpr_unref_type) GetProcAddress(library, "gpr_unref");
-  gpr_stats_init_import = (gpr_stats_init_type) GetProcAddress(library, "gpr_stats_init");
-  gpr_stats_inc_import = (gpr_stats_inc_type) GetProcAddress(library, "gpr_stats_inc");
-  gpr_stats_read_import = (gpr_stats_read_type) GetProcAddress(library, "gpr_stats_read");
-  gpr_time_0_import = (gpr_time_0_type) GetProcAddress(library, "gpr_time_0");
-  gpr_inf_future_import = (gpr_inf_future_type) GetProcAddress(library, "gpr_inf_future");
-  gpr_inf_past_import = (gpr_inf_past_type) GetProcAddress(library, "gpr_inf_past");
-  gpr_time_init_import = (gpr_time_init_type) GetProcAddress(library, "gpr_time_init");
-  gpr_now_import = (gpr_now_type) GetProcAddress(library, "gpr_now");
-  gpr_convert_clock_type_import = (gpr_convert_clock_type_type) GetProcAddress(library, "gpr_convert_clock_type");
-  gpr_time_cmp_import = (gpr_time_cmp_type) GetProcAddress(library, "gpr_time_cmp");
-  gpr_time_max_import = (gpr_time_max_type) GetProcAddress(library, "gpr_time_max");
-  gpr_time_min_import = (gpr_time_min_type) GetProcAddress(library, "gpr_time_min");
-  gpr_time_add_import = (gpr_time_add_type) GetProcAddress(library, "gpr_time_add");
-  gpr_time_sub_import = (gpr_time_sub_type) GetProcAddress(library, "gpr_time_sub");
-  gpr_time_from_micros_import = (gpr_time_from_micros_type) GetProcAddress(library, "gpr_time_from_micros");
-  gpr_time_from_nanos_import = (gpr_time_from_nanos_type) GetProcAddress(library, "gpr_time_from_nanos");
-  gpr_time_from_millis_import = (gpr_time_from_millis_type) GetProcAddress(library, "gpr_time_from_millis");
-  gpr_time_from_seconds_import = (gpr_time_from_seconds_type) GetProcAddress(library, "gpr_time_from_seconds");
-  gpr_time_from_minutes_import = (gpr_time_from_minutes_type) GetProcAddress(library, "gpr_time_from_minutes");
-  gpr_time_from_hours_import = (gpr_time_from_hours_type) GetProcAddress(library, "gpr_time_from_hours");
-  gpr_time_to_millis_import = (gpr_time_to_millis_type) GetProcAddress(library, "gpr_time_to_millis");
-  gpr_time_similar_import = (gpr_time_similar_type) GetProcAddress(library, "gpr_time_similar");
-  gpr_sleep_until_import = (gpr_sleep_until_type) GetProcAddress(library, "gpr_sleep_until");
-  gpr_timespec_to_micros_import = (gpr_timespec_to_micros_type) GetProcAddress(library, "gpr_timespec_to_micros");
   gpr_avl_create_import = (gpr_avl_create_type) GetProcAddress(library, "gpr_avl_create");
   gpr_avl_ref_import = (gpr_avl_ref_type) GetProcAddress(library, "gpr_avl_ref");
   gpr_avl_unref_import = (gpr_avl_unref_type) GetProcAddress(library, "gpr_avl_unref");
@@ -565,7 +479,41 @@
   gpr_histogram_merge_contents_import = (gpr_histogram_merge_contents_type) GetProcAddress(library, "gpr_histogram_merge_contents");
   gpr_join_host_port_import = (gpr_join_host_port_type) GetProcAddress(library, "gpr_join_host_port");
   gpr_split_host_port_import = (gpr_split_host_port_type) GetProcAddress(library, "gpr_split_host_port");
+  gpr_log_import = (gpr_log_type) GetProcAddress(library, "gpr_log");
+  gpr_log_message_import = (gpr_log_message_type) GetProcAddress(library, "gpr_log_message");
+  gpr_set_log_verbosity_import = (gpr_set_log_verbosity_type) GetProcAddress(library, "gpr_set_log_verbosity");
+  gpr_log_verbosity_init_import = (gpr_log_verbosity_init_type) GetProcAddress(library, "gpr_log_verbosity_init");
+  gpr_set_log_function_import = (gpr_set_log_function_type) GetProcAddress(library, "gpr_set_log_function");
   gpr_format_message_import = (gpr_format_message_type) GetProcAddress(library, "gpr_format_message");
+  gpr_slice_ref_import = (gpr_slice_ref_type) GetProcAddress(library, "gpr_slice_ref");
+  gpr_slice_unref_import = (gpr_slice_unref_type) GetProcAddress(library, "gpr_slice_unref");
+  gpr_slice_new_import = (gpr_slice_new_type) GetProcAddress(library, "gpr_slice_new");
+  gpr_slice_new_with_user_data_import = (gpr_slice_new_with_user_data_type) GetProcAddress(library, "gpr_slice_new_with_user_data");
+  gpr_slice_new_with_len_import = (gpr_slice_new_with_len_type) GetProcAddress(library, "gpr_slice_new_with_len");
+  gpr_slice_malloc_import = (gpr_slice_malloc_type) GetProcAddress(library, "gpr_slice_malloc");
+  gpr_slice_from_copied_string_import = (gpr_slice_from_copied_string_type) GetProcAddress(library, "gpr_slice_from_copied_string");
+  gpr_slice_from_copied_buffer_import = (gpr_slice_from_copied_buffer_type) GetProcAddress(library, "gpr_slice_from_copied_buffer");
+  gpr_slice_from_static_string_import = (gpr_slice_from_static_string_type) GetProcAddress(library, "gpr_slice_from_static_string");
+  gpr_slice_sub_import = (gpr_slice_sub_type) GetProcAddress(library, "gpr_slice_sub");
+  gpr_slice_sub_no_ref_import = (gpr_slice_sub_no_ref_type) GetProcAddress(library, "gpr_slice_sub_no_ref");
+  gpr_slice_split_tail_import = (gpr_slice_split_tail_type) GetProcAddress(library, "gpr_slice_split_tail");
+  gpr_slice_split_head_import = (gpr_slice_split_head_type) GetProcAddress(library, "gpr_slice_split_head");
+  gpr_empty_slice_import = (gpr_empty_slice_type) GetProcAddress(library, "gpr_empty_slice");
+  gpr_slice_cmp_import = (gpr_slice_cmp_type) GetProcAddress(library, "gpr_slice_cmp");
+  gpr_slice_str_cmp_import = (gpr_slice_str_cmp_type) GetProcAddress(library, "gpr_slice_str_cmp");
+  gpr_slice_buffer_init_import = (gpr_slice_buffer_init_type) GetProcAddress(library, "gpr_slice_buffer_init");
+  gpr_slice_buffer_destroy_import = (gpr_slice_buffer_destroy_type) GetProcAddress(library, "gpr_slice_buffer_destroy");
+  gpr_slice_buffer_add_import = (gpr_slice_buffer_add_type) GetProcAddress(library, "gpr_slice_buffer_add");
+  gpr_slice_buffer_add_indexed_import = (gpr_slice_buffer_add_indexed_type) GetProcAddress(library, "gpr_slice_buffer_add_indexed");
+  gpr_slice_buffer_addn_import = (gpr_slice_buffer_addn_type) GetProcAddress(library, "gpr_slice_buffer_addn");
+  gpr_slice_buffer_tiny_add_import = (gpr_slice_buffer_tiny_add_type) GetProcAddress(library, "gpr_slice_buffer_tiny_add");
+  gpr_slice_buffer_pop_import = (gpr_slice_buffer_pop_type) GetProcAddress(library, "gpr_slice_buffer_pop");
+  gpr_slice_buffer_reset_and_unref_import = (gpr_slice_buffer_reset_and_unref_type) GetProcAddress(library, "gpr_slice_buffer_reset_and_unref");
+  gpr_slice_buffer_swap_import = (gpr_slice_buffer_swap_type) GetProcAddress(library, "gpr_slice_buffer_swap");
+  gpr_slice_buffer_move_into_import = (gpr_slice_buffer_move_into_type) GetProcAddress(library, "gpr_slice_buffer_move_into");
+  gpr_slice_buffer_trim_end_import = (gpr_slice_buffer_trim_end_type) GetProcAddress(library, "gpr_slice_buffer_trim_end");
+  gpr_slice_buffer_move_first_import = (gpr_slice_buffer_move_first_type) GetProcAddress(library, "gpr_slice_buffer_move_first");
+  gpr_slice_buffer_take_first_import = (gpr_slice_buffer_take_first_type) GetProcAddress(library, "gpr_slice_buffer_take_first");
   gpr_strdup_import = (gpr_strdup_type) GetProcAddress(library, "gpr_strdup");
   gpr_asprintf_import = (gpr_asprintf_type) GetProcAddress(library, "gpr_asprintf");
   gpr_subprocess_binary_extension_import = (gpr_subprocess_binary_extension_type) GetProcAddress(library, "gpr_subprocess_binary_extension");
@@ -573,6 +521,29 @@
   gpr_subprocess_destroy_import = (gpr_subprocess_destroy_type) GetProcAddress(library, "gpr_subprocess_destroy");
   gpr_subprocess_join_import = (gpr_subprocess_join_type) GetProcAddress(library, "gpr_subprocess_join");
   gpr_subprocess_interrupt_import = (gpr_subprocess_interrupt_type) GetProcAddress(library, "gpr_subprocess_interrupt");
+  gpr_mu_init_import = (gpr_mu_init_type) GetProcAddress(library, "gpr_mu_init");
+  gpr_mu_destroy_import = (gpr_mu_destroy_type) GetProcAddress(library, "gpr_mu_destroy");
+  gpr_mu_lock_import = (gpr_mu_lock_type) GetProcAddress(library, "gpr_mu_lock");
+  gpr_mu_unlock_import = (gpr_mu_unlock_type) GetProcAddress(library, "gpr_mu_unlock");
+  gpr_mu_trylock_import = (gpr_mu_trylock_type) GetProcAddress(library, "gpr_mu_trylock");
+  gpr_cv_init_import = (gpr_cv_init_type) GetProcAddress(library, "gpr_cv_init");
+  gpr_cv_destroy_import = (gpr_cv_destroy_type) GetProcAddress(library, "gpr_cv_destroy");
+  gpr_cv_wait_import = (gpr_cv_wait_type) GetProcAddress(library, "gpr_cv_wait");
+  gpr_cv_signal_import = (gpr_cv_signal_type) GetProcAddress(library, "gpr_cv_signal");
+  gpr_cv_broadcast_import = (gpr_cv_broadcast_type) GetProcAddress(library, "gpr_cv_broadcast");
+  gpr_once_init_import = (gpr_once_init_type) GetProcAddress(library, "gpr_once_init");
+  gpr_event_init_import = (gpr_event_init_type) GetProcAddress(library, "gpr_event_init");
+  gpr_event_set_import = (gpr_event_set_type) GetProcAddress(library, "gpr_event_set");
+  gpr_event_get_import = (gpr_event_get_type) GetProcAddress(library, "gpr_event_get");
+  gpr_event_wait_import = (gpr_event_wait_type) GetProcAddress(library, "gpr_event_wait");
+  gpr_ref_init_import = (gpr_ref_init_type) GetProcAddress(library, "gpr_ref_init");
+  gpr_ref_import = (gpr_ref_type) GetProcAddress(library, "gpr_ref");
+  gpr_ref_non_zero_import = (gpr_ref_non_zero_type) GetProcAddress(library, "gpr_ref_non_zero");
+  gpr_refn_import = (gpr_refn_type) GetProcAddress(library, "gpr_refn");
+  gpr_unref_import = (gpr_unref_type) GetProcAddress(library, "gpr_unref");
+  gpr_stats_init_import = (gpr_stats_init_type) GetProcAddress(library, "gpr_stats_init");
+  gpr_stats_inc_import = (gpr_stats_inc_type) GetProcAddress(library, "gpr_stats_inc");
+  gpr_stats_read_import = (gpr_stats_read_type) GetProcAddress(library, "gpr_stats_read");
   gpr_thd_new_import = (gpr_thd_new_type) GetProcAddress(library, "gpr_thd_new");
   gpr_thd_options_default_import = (gpr_thd_options_default_type) GetProcAddress(library, "gpr_thd_options_default");
   gpr_thd_options_set_detached_import = (gpr_thd_options_set_detached_type) GetProcAddress(library, "gpr_thd_options_set_detached");
@@ -581,6 +552,27 @@
   gpr_thd_options_is_joinable_import = (gpr_thd_options_is_joinable_type) GetProcAddress(library, "gpr_thd_options_is_joinable");
   gpr_thd_currentid_import = (gpr_thd_currentid_type) GetProcAddress(library, "gpr_thd_currentid");
   gpr_thd_join_import = (gpr_thd_join_type) GetProcAddress(library, "gpr_thd_join");
+  gpr_time_0_import = (gpr_time_0_type) GetProcAddress(library, "gpr_time_0");
+  gpr_inf_future_import = (gpr_inf_future_type) GetProcAddress(library, "gpr_inf_future");
+  gpr_inf_past_import = (gpr_inf_past_type) GetProcAddress(library, "gpr_inf_past");
+  gpr_time_init_import = (gpr_time_init_type) GetProcAddress(library, "gpr_time_init");
+  gpr_now_import = (gpr_now_type) GetProcAddress(library, "gpr_now");
+  gpr_convert_clock_type_import = (gpr_convert_clock_type_type) GetProcAddress(library, "gpr_convert_clock_type");
+  gpr_time_cmp_import = (gpr_time_cmp_type) GetProcAddress(library, "gpr_time_cmp");
+  gpr_time_max_import = (gpr_time_max_type) GetProcAddress(library, "gpr_time_max");
+  gpr_time_min_import = (gpr_time_min_type) GetProcAddress(library, "gpr_time_min");
+  gpr_time_add_import = (gpr_time_add_type) GetProcAddress(library, "gpr_time_add");
+  gpr_time_sub_import = (gpr_time_sub_type) GetProcAddress(library, "gpr_time_sub");
+  gpr_time_from_micros_import = (gpr_time_from_micros_type) GetProcAddress(library, "gpr_time_from_micros");
+  gpr_time_from_nanos_import = (gpr_time_from_nanos_type) GetProcAddress(library, "gpr_time_from_nanos");
+  gpr_time_from_millis_import = (gpr_time_from_millis_type) GetProcAddress(library, "gpr_time_from_millis");
+  gpr_time_from_seconds_import = (gpr_time_from_seconds_type) GetProcAddress(library, "gpr_time_from_seconds");
+  gpr_time_from_minutes_import = (gpr_time_from_minutes_type) GetProcAddress(library, "gpr_time_from_minutes");
+  gpr_time_from_hours_import = (gpr_time_from_hours_type) GetProcAddress(library, "gpr_time_from_hours");
+  gpr_time_to_millis_import = (gpr_time_to_millis_type) GetProcAddress(library, "gpr_time_to_millis");
+  gpr_time_similar_import = (gpr_time_similar_type) GetProcAddress(library, "gpr_time_similar");
+  gpr_sleep_until_import = (gpr_sleep_until_type) GetProcAddress(library, "gpr_sleep_until");
+  gpr_timespec_to_micros_import = (gpr_timespec_to_micros_type) GetProcAddress(library, "gpr_timespec_to_micros");
 }
 
 #endif /* GPR_WINDOWS */
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index 14da637..00a67b0 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -40,28 +40,58 @@
 
 #include <windows.h>
 
+#include <grpc/byte_buffer.h>
 #include <grpc/census.h>
 #include <grpc/compression.h>
 #include <grpc/grpc.h>
 #include <grpc/grpc_posix.h>
 #include <grpc/grpc_security.h>
-#include <grpc/impl/codegen/alloc.h>
-#include <grpc/impl/codegen/byte_buffer.h>
-#include <grpc/impl/codegen/log.h>
-#include <grpc/impl/codegen/slice.h>
-#include <grpc/impl/codegen/slice_buffer.h>
-#include <grpc/impl/codegen/sync.h>
-#include <grpc/impl/codegen/time.h>
+#include <grpc/support/alloc.h>
 #include <grpc/support/avl.h>
 #include <grpc/support/cmdline.h>
 #include <grpc/support/cpu.h>
 #include <grpc/support/histogram.h>
 #include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
 #include <grpc/support/log_windows.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/slice_buffer.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/subprocess.h>
+#include <grpc/support/sync.h>
 #include <grpc/support/thd.h>
+#include <grpc/support/time.h>
 
+typedef grpc_byte_buffer *(*grpc_raw_byte_buffer_create_type)(gpr_slice *slices, size_t nslices);
+extern grpc_raw_byte_buffer_create_type grpc_raw_byte_buffer_create_import;
+#define grpc_raw_byte_buffer_create grpc_raw_byte_buffer_create_import
+typedef grpc_byte_buffer *(*grpc_raw_compressed_byte_buffer_create_type)(gpr_slice *slices, size_t nslices, grpc_compression_algorithm compression);
+extern grpc_raw_compressed_byte_buffer_create_type grpc_raw_compressed_byte_buffer_create_import;
+#define grpc_raw_compressed_byte_buffer_create grpc_raw_compressed_byte_buffer_create_import
+typedef grpc_byte_buffer *(*grpc_byte_buffer_copy_type)(grpc_byte_buffer *bb);
+extern grpc_byte_buffer_copy_type grpc_byte_buffer_copy_import;
+#define grpc_byte_buffer_copy grpc_byte_buffer_copy_import
+typedef size_t(*grpc_byte_buffer_length_type)(grpc_byte_buffer *bb);
+extern grpc_byte_buffer_length_type grpc_byte_buffer_length_import;
+#define grpc_byte_buffer_length grpc_byte_buffer_length_import
+typedef void(*grpc_byte_buffer_destroy_type)(grpc_byte_buffer *byte_buffer);
+extern grpc_byte_buffer_destroy_type grpc_byte_buffer_destroy_import;
+#define grpc_byte_buffer_destroy grpc_byte_buffer_destroy_import
+typedef int(*grpc_byte_buffer_reader_init_type)(grpc_byte_buffer_reader *reader, grpc_byte_buffer *buffer);
+extern grpc_byte_buffer_reader_init_type grpc_byte_buffer_reader_init_import;
+#define grpc_byte_buffer_reader_init grpc_byte_buffer_reader_init_import
+typedef void(*grpc_byte_buffer_reader_destroy_type)(grpc_byte_buffer_reader *reader);
+extern grpc_byte_buffer_reader_destroy_type grpc_byte_buffer_reader_destroy_import;
+#define grpc_byte_buffer_reader_destroy grpc_byte_buffer_reader_destroy_import
+typedef int(*grpc_byte_buffer_reader_next_type)(grpc_byte_buffer_reader *reader, gpr_slice *slice);
+extern grpc_byte_buffer_reader_next_type grpc_byte_buffer_reader_next_import;
+#define grpc_byte_buffer_reader_next grpc_byte_buffer_reader_next_import
+typedef gpr_slice(*grpc_byte_buffer_reader_readall_type)(grpc_byte_buffer_reader *reader);
+extern grpc_byte_buffer_reader_readall_type grpc_byte_buffer_reader_readall_import;
+#define grpc_byte_buffer_reader_readall grpc_byte_buffer_reader_readall_import
+typedef grpc_byte_buffer *(*grpc_raw_byte_buffer_from_reader_type)(grpc_byte_buffer_reader *reader);
+extern grpc_raw_byte_buffer_from_reader_type grpc_raw_byte_buffer_from_reader_import;
+#define grpc_raw_byte_buffer_from_reader grpc_raw_byte_buffer_from_reader_import
 typedef int(*census_initialize_type)(int features);
 extern census_initialize_type census_initialize_import;
 #define census_initialize census_initialize_import
@@ -134,33 +164,18 @@
 typedef void(*census_trace_scan_end_type)();
 extern census_trace_scan_end_type census_trace_scan_end_import;
 #define census_trace_scan_end census_trace_scan_end_import
+typedef int32_t(*census_define_resource_type)(const uint8_t *resource_pb, size_t resource_pb_size);
+extern census_define_resource_type census_define_resource_import;
+#define census_define_resource census_define_resource_import
+typedef void(*census_delete_resource_type)(int32_t resource_id);
+extern census_delete_resource_type census_delete_resource_import;
+#define census_delete_resource census_delete_resource_import
+typedef int32_t(*census_resource_id_type)(const char *name);
+extern census_resource_id_type census_resource_id_import;
+#define census_resource_id census_resource_id_import
 typedef void(*census_record_values_type)(census_context *context, census_value *values, size_t nvalues);
 extern census_record_values_type census_record_values_import;
 #define census_record_values census_record_values_import
-typedef census_view *(*census_view_create_type)(uint32_t metric_id, const census_context *tags, const census_aggregation *aggregations, size_t naggregations);
-extern census_view_create_type census_view_create_import;
-#define census_view_create census_view_create_import
-typedef void(*census_view_delete_type)(census_view *view);
-extern census_view_delete_type census_view_delete_import;
-#define census_view_delete census_view_delete_import
-typedef size_t(*census_view_metric_type)(const census_view *view);
-extern census_view_metric_type census_view_metric_import;
-#define census_view_metric census_view_metric_import
-typedef size_t(*census_view_naggregations_type)(const census_view *view);
-extern census_view_naggregations_type census_view_naggregations_import;
-#define census_view_naggregations census_view_naggregations_import
-typedef const census_context *(*census_view_tags_type)(const census_view *view);
-extern census_view_tags_type census_view_tags_import;
-#define census_view_tags census_view_tags_import
-typedef const census_aggregation *(*census_view_aggregrations_type)(const census_view *view);
-extern census_view_aggregrations_type census_view_aggregrations_import;
-#define census_view_aggregrations census_view_aggregrations_import
-typedef const census_view_data *(*census_view_get_data_type)(const census_view *view);
-extern census_view_get_data_type census_view_get_data_import;
-#define census_view_get_data census_view_get_data_import
-typedef void(*census_view_reset_type)(census_view *view);
-extern census_view_reset_type census_view_reset_import;
-#define census_view_reset census_view_reset_import
 typedef int(*grpc_compression_algorithm_parse_type)(const char *name, size_t name_length, grpc_compression_algorithm *algorithm);
 extern grpc_compression_algorithm_parse_type grpc_compression_algorithm_parse_import;
 #define grpc_compression_algorithm_parse grpc_compression_algorithm_parse_import
@@ -206,6 +221,9 @@
 typedef const char *(*grpc_version_string_type)(void);
 extern grpc_version_string_type grpc_version_string_import;
 #define grpc_version_string grpc_version_string_import
+typedef const char *(*grpc_g_stands_for_type)(void);
+extern grpc_g_stands_for_type grpc_g_stands_for_import;
+#define grpc_g_stands_for grpc_g_stands_for_import
 typedef grpc_completion_queue *(*grpc_completion_queue_create_type)(void *reserved);
 extern grpc_completion_queue_create_type grpc_completion_queue_create_import;
 #define grpc_completion_queue_create grpc_completion_queue_create_import
@@ -332,7 +350,7 @@
 typedef grpc_channel *(*grpc_insecure_channel_create_from_fd_type)(const char *target, int fd, const grpc_channel_args *args);
 extern grpc_insecure_channel_create_from_fd_type grpc_insecure_channel_create_from_fd_import;
 #define grpc_insecure_channel_create_from_fd grpc_insecure_channel_create_from_fd_import
-typedef void(*grpc_server_add_insecure_channel_from_fd_type)(grpc_server *server, grpc_completion_queue *cq, int fd);
+typedef void(*grpc_server_add_insecure_channel_from_fd_type)(grpc_server *server, void *reserved, int fd);
 extern grpc_server_add_insecure_channel_from_fd_type grpc_server_add_insecure_channel_from_fd_import;
 #define grpc_server_add_insecure_channel_from_fd grpc_server_add_insecure_channel_from_fd_import
 typedef void(*grpc_use_signal_type)(int signum);
@@ -455,270 +473,6 @@
 typedef gpr_allocation_functions(*gpr_get_allocation_functions_type)();
 extern gpr_get_allocation_functions_type gpr_get_allocation_functions_import;
 #define gpr_get_allocation_functions gpr_get_allocation_functions_import
-typedef grpc_byte_buffer *(*grpc_raw_byte_buffer_create_type)(gpr_slice *slices, size_t nslices);
-extern grpc_raw_byte_buffer_create_type grpc_raw_byte_buffer_create_import;
-#define grpc_raw_byte_buffer_create grpc_raw_byte_buffer_create_import
-typedef grpc_byte_buffer *(*grpc_raw_compressed_byte_buffer_create_type)(gpr_slice *slices, size_t nslices, grpc_compression_algorithm compression);
-extern grpc_raw_compressed_byte_buffer_create_type grpc_raw_compressed_byte_buffer_create_import;
-#define grpc_raw_compressed_byte_buffer_create grpc_raw_compressed_byte_buffer_create_import
-typedef grpc_byte_buffer *(*grpc_byte_buffer_copy_type)(grpc_byte_buffer *bb);
-extern grpc_byte_buffer_copy_type grpc_byte_buffer_copy_import;
-#define grpc_byte_buffer_copy grpc_byte_buffer_copy_import
-typedef size_t(*grpc_byte_buffer_length_type)(grpc_byte_buffer *bb);
-extern grpc_byte_buffer_length_type grpc_byte_buffer_length_import;
-#define grpc_byte_buffer_length grpc_byte_buffer_length_import
-typedef void(*grpc_byte_buffer_destroy_type)(grpc_byte_buffer *byte_buffer);
-extern grpc_byte_buffer_destroy_type grpc_byte_buffer_destroy_import;
-#define grpc_byte_buffer_destroy grpc_byte_buffer_destroy_import
-typedef int(*grpc_byte_buffer_reader_init_type)(grpc_byte_buffer_reader *reader, grpc_byte_buffer *buffer);
-extern grpc_byte_buffer_reader_init_type grpc_byte_buffer_reader_init_import;
-#define grpc_byte_buffer_reader_init grpc_byte_buffer_reader_init_import
-typedef void(*grpc_byte_buffer_reader_destroy_type)(grpc_byte_buffer_reader *reader);
-extern grpc_byte_buffer_reader_destroy_type grpc_byte_buffer_reader_destroy_import;
-#define grpc_byte_buffer_reader_destroy grpc_byte_buffer_reader_destroy_import
-typedef int(*grpc_byte_buffer_reader_next_type)(grpc_byte_buffer_reader *reader, gpr_slice *slice);
-extern grpc_byte_buffer_reader_next_type grpc_byte_buffer_reader_next_import;
-#define grpc_byte_buffer_reader_next grpc_byte_buffer_reader_next_import
-typedef gpr_slice(*grpc_byte_buffer_reader_readall_type)(grpc_byte_buffer_reader *reader);
-extern grpc_byte_buffer_reader_readall_type grpc_byte_buffer_reader_readall_import;
-#define grpc_byte_buffer_reader_readall grpc_byte_buffer_reader_readall_import
-typedef grpc_byte_buffer *(*grpc_raw_byte_buffer_from_reader_type)(grpc_byte_buffer_reader *reader);
-extern grpc_raw_byte_buffer_from_reader_type grpc_raw_byte_buffer_from_reader_import;
-#define grpc_raw_byte_buffer_from_reader grpc_raw_byte_buffer_from_reader_import
-typedef void(*gpr_log_type)(const char *file, int line, gpr_log_severity severity, const char *format, ...) GPRC_PRINT_FORMAT_CHECK(4, 5);
-extern gpr_log_type gpr_log_import;
-#define gpr_log gpr_log_import
-typedef void(*gpr_log_message_type)(const char *file, int line, gpr_log_severity severity, const char *message);
-extern gpr_log_message_type gpr_log_message_import;
-#define gpr_log_message gpr_log_message_import
-typedef void(*gpr_set_log_verbosity_type)(gpr_log_severity min_severity_to_print);
-extern gpr_set_log_verbosity_type gpr_set_log_verbosity_import;
-#define gpr_set_log_verbosity gpr_set_log_verbosity_import
-typedef void(*gpr_log_verbosity_init_type)();
-extern gpr_log_verbosity_init_type gpr_log_verbosity_init_import;
-#define gpr_log_verbosity_init gpr_log_verbosity_init_import
-typedef void(*gpr_set_log_function_type)(gpr_log_func func);
-extern gpr_set_log_function_type gpr_set_log_function_import;
-#define gpr_set_log_function gpr_set_log_function_import
-typedef gpr_slice(*gpr_slice_ref_type)(gpr_slice s);
-extern gpr_slice_ref_type gpr_slice_ref_import;
-#define gpr_slice_ref gpr_slice_ref_import
-typedef void(*gpr_slice_unref_type)(gpr_slice s);
-extern gpr_slice_unref_type gpr_slice_unref_import;
-#define gpr_slice_unref gpr_slice_unref_import
-typedef gpr_slice(*gpr_slice_new_type)(void *p, size_t len, void (*destroy)(void *));
-extern gpr_slice_new_type gpr_slice_new_import;
-#define gpr_slice_new gpr_slice_new_import
-typedef gpr_slice(*gpr_slice_new_with_user_data_type)(void *p, size_t len, void (*destroy)(void *), void *user_data);
-extern gpr_slice_new_with_user_data_type gpr_slice_new_with_user_data_import;
-#define gpr_slice_new_with_user_data gpr_slice_new_with_user_data_import
-typedef gpr_slice(*gpr_slice_new_with_len_type)(void *p, size_t len, void (*destroy)(void *, size_t));
-extern gpr_slice_new_with_len_type gpr_slice_new_with_len_import;
-#define gpr_slice_new_with_len gpr_slice_new_with_len_import
-typedef gpr_slice(*gpr_slice_malloc_type)(size_t length);
-extern gpr_slice_malloc_type gpr_slice_malloc_import;
-#define gpr_slice_malloc gpr_slice_malloc_import
-typedef gpr_slice(*gpr_slice_from_copied_string_type)(const char *source);
-extern gpr_slice_from_copied_string_type gpr_slice_from_copied_string_import;
-#define gpr_slice_from_copied_string gpr_slice_from_copied_string_import
-typedef gpr_slice(*gpr_slice_from_copied_buffer_type)(const char *source, size_t len);
-extern gpr_slice_from_copied_buffer_type gpr_slice_from_copied_buffer_import;
-#define gpr_slice_from_copied_buffer gpr_slice_from_copied_buffer_import
-typedef gpr_slice(*gpr_slice_from_static_string_type)(const char *source);
-extern gpr_slice_from_static_string_type gpr_slice_from_static_string_import;
-#define gpr_slice_from_static_string gpr_slice_from_static_string_import
-typedef gpr_slice(*gpr_slice_sub_type)(gpr_slice s, size_t begin, size_t end);
-extern gpr_slice_sub_type gpr_slice_sub_import;
-#define gpr_slice_sub gpr_slice_sub_import
-typedef gpr_slice(*gpr_slice_sub_no_ref_type)(gpr_slice s, size_t begin, size_t end);
-extern gpr_slice_sub_no_ref_type gpr_slice_sub_no_ref_import;
-#define gpr_slice_sub_no_ref gpr_slice_sub_no_ref_import
-typedef gpr_slice(*gpr_slice_split_tail_type)(gpr_slice *s, size_t split);
-extern gpr_slice_split_tail_type gpr_slice_split_tail_import;
-#define gpr_slice_split_tail gpr_slice_split_tail_import
-typedef gpr_slice(*gpr_slice_split_head_type)(gpr_slice *s, size_t split);
-extern gpr_slice_split_head_type gpr_slice_split_head_import;
-#define gpr_slice_split_head gpr_slice_split_head_import
-typedef gpr_slice(*gpr_empty_slice_type)(void);
-extern gpr_empty_slice_type gpr_empty_slice_import;
-#define gpr_empty_slice gpr_empty_slice_import
-typedef int(*gpr_slice_cmp_type)(gpr_slice a, gpr_slice b);
-extern gpr_slice_cmp_type gpr_slice_cmp_import;
-#define gpr_slice_cmp gpr_slice_cmp_import
-typedef int(*gpr_slice_str_cmp_type)(gpr_slice a, const char *b);
-extern gpr_slice_str_cmp_type gpr_slice_str_cmp_import;
-#define gpr_slice_str_cmp gpr_slice_str_cmp_import
-typedef void(*gpr_slice_buffer_init_type)(gpr_slice_buffer *sb);
-extern gpr_slice_buffer_init_type gpr_slice_buffer_init_import;
-#define gpr_slice_buffer_init gpr_slice_buffer_init_import
-typedef void(*gpr_slice_buffer_destroy_type)(gpr_slice_buffer *sb);
-extern gpr_slice_buffer_destroy_type gpr_slice_buffer_destroy_import;
-#define gpr_slice_buffer_destroy gpr_slice_buffer_destroy_import
-typedef void(*gpr_slice_buffer_add_type)(gpr_slice_buffer *sb, gpr_slice slice);
-extern gpr_slice_buffer_add_type gpr_slice_buffer_add_import;
-#define gpr_slice_buffer_add gpr_slice_buffer_add_import
-typedef size_t(*gpr_slice_buffer_add_indexed_type)(gpr_slice_buffer *sb, gpr_slice slice);
-extern gpr_slice_buffer_add_indexed_type gpr_slice_buffer_add_indexed_import;
-#define gpr_slice_buffer_add_indexed gpr_slice_buffer_add_indexed_import
-typedef void(*gpr_slice_buffer_addn_type)(gpr_slice_buffer *sb, gpr_slice *slices, size_t n);
-extern gpr_slice_buffer_addn_type gpr_slice_buffer_addn_import;
-#define gpr_slice_buffer_addn gpr_slice_buffer_addn_import
-typedef uint8_t *(*gpr_slice_buffer_tiny_add_type)(gpr_slice_buffer *sb, size_t len);
-extern gpr_slice_buffer_tiny_add_type gpr_slice_buffer_tiny_add_import;
-#define gpr_slice_buffer_tiny_add gpr_slice_buffer_tiny_add_import
-typedef void(*gpr_slice_buffer_pop_type)(gpr_slice_buffer *sb);
-extern gpr_slice_buffer_pop_type gpr_slice_buffer_pop_import;
-#define gpr_slice_buffer_pop gpr_slice_buffer_pop_import
-typedef void(*gpr_slice_buffer_reset_and_unref_type)(gpr_slice_buffer *sb);
-extern gpr_slice_buffer_reset_and_unref_type gpr_slice_buffer_reset_and_unref_import;
-#define gpr_slice_buffer_reset_and_unref gpr_slice_buffer_reset_and_unref_import
-typedef void(*gpr_slice_buffer_swap_type)(gpr_slice_buffer *a, gpr_slice_buffer *b);
-extern gpr_slice_buffer_swap_type gpr_slice_buffer_swap_import;
-#define gpr_slice_buffer_swap gpr_slice_buffer_swap_import
-typedef void(*gpr_slice_buffer_move_into_type)(gpr_slice_buffer *src, gpr_slice_buffer *dst);
-extern gpr_slice_buffer_move_into_type gpr_slice_buffer_move_into_import;
-#define gpr_slice_buffer_move_into gpr_slice_buffer_move_into_import
-typedef void(*gpr_slice_buffer_trim_end_type)(gpr_slice_buffer *src, size_t n, gpr_slice_buffer *garbage);
-extern gpr_slice_buffer_trim_end_type gpr_slice_buffer_trim_end_import;
-#define gpr_slice_buffer_trim_end gpr_slice_buffer_trim_end_import
-typedef void(*gpr_slice_buffer_move_first_type)(gpr_slice_buffer *src, size_t n, gpr_slice_buffer *dst);
-extern gpr_slice_buffer_move_first_type gpr_slice_buffer_move_first_import;
-#define gpr_slice_buffer_move_first gpr_slice_buffer_move_first_import
-typedef gpr_slice(*gpr_slice_buffer_take_first_type)(gpr_slice_buffer *src);
-extern gpr_slice_buffer_take_first_type gpr_slice_buffer_take_first_import;
-#define gpr_slice_buffer_take_first gpr_slice_buffer_take_first_import
-typedef void(*gpr_mu_init_type)(gpr_mu *mu);
-extern gpr_mu_init_type gpr_mu_init_import;
-#define gpr_mu_init gpr_mu_init_import
-typedef void(*gpr_mu_destroy_type)(gpr_mu *mu);
-extern gpr_mu_destroy_type gpr_mu_destroy_import;
-#define gpr_mu_destroy gpr_mu_destroy_import
-typedef void(*gpr_mu_lock_type)(gpr_mu *mu);
-extern gpr_mu_lock_type gpr_mu_lock_import;
-#define gpr_mu_lock gpr_mu_lock_import
-typedef void(*gpr_mu_unlock_type)(gpr_mu *mu);
-extern gpr_mu_unlock_type gpr_mu_unlock_import;
-#define gpr_mu_unlock gpr_mu_unlock_import
-typedef int(*gpr_mu_trylock_type)(gpr_mu *mu);
-extern gpr_mu_trylock_type gpr_mu_trylock_import;
-#define gpr_mu_trylock gpr_mu_trylock_import
-typedef void(*gpr_cv_init_type)(gpr_cv *cv);
-extern gpr_cv_init_type gpr_cv_init_import;
-#define gpr_cv_init gpr_cv_init_import
-typedef void(*gpr_cv_destroy_type)(gpr_cv *cv);
-extern gpr_cv_destroy_type gpr_cv_destroy_import;
-#define gpr_cv_destroy gpr_cv_destroy_import
-typedef int(*gpr_cv_wait_type)(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline);
-extern gpr_cv_wait_type gpr_cv_wait_import;
-#define gpr_cv_wait gpr_cv_wait_import
-typedef void(*gpr_cv_signal_type)(gpr_cv *cv);
-extern gpr_cv_signal_type gpr_cv_signal_import;
-#define gpr_cv_signal gpr_cv_signal_import
-typedef void(*gpr_cv_broadcast_type)(gpr_cv *cv);
-extern gpr_cv_broadcast_type gpr_cv_broadcast_import;
-#define gpr_cv_broadcast gpr_cv_broadcast_import
-typedef void(*gpr_once_init_type)(gpr_once *once, void (*init_routine)(void));
-extern gpr_once_init_type gpr_once_init_import;
-#define gpr_once_init gpr_once_init_import
-typedef void(*gpr_event_init_type)(gpr_event *ev);
-extern gpr_event_init_type gpr_event_init_import;
-#define gpr_event_init gpr_event_init_import
-typedef void(*gpr_event_set_type)(gpr_event *ev, void *value);
-extern gpr_event_set_type gpr_event_set_import;
-#define gpr_event_set gpr_event_set_import
-typedef void *(*gpr_event_get_type)(gpr_event *ev);
-extern gpr_event_get_type gpr_event_get_import;
-#define gpr_event_get gpr_event_get_import
-typedef void *(*gpr_event_wait_type)(gpr_event *ev, gpr_timespec abs_deadline);
-extern gpr_event_wait_type gpr_event_wait_import;
-#define gpr_event_wait gpr_event_wait_import
-typedef void(*gpr_ref_init_type)(gpr_refcount *r, int n);
-extern gpr_ref_init_type gpr_ref_init_import;
-#define gpr_ref_init gpr_ref_init_import
-typedef void(*gpr_ref_type)(gpr_refcount *r);
-extern gpr_ref_type gpr_ref_import;
-#define gpr_ref gpr_ref_import
-typedef void(*gpr_ref_non_zero_type)(gpr_refcount *r);
-extern gpr_ref_non_zero_type gpr_ref_non_zero_import;
-#define gpr_ref_non_zero gpr_ref_non_zero_import
-typedef void(*gpr_refn_type)(gpr_refcount *r, int n);
-extern gpr_refn_type gpr_refn_import;
-#define gpr_refn gpr_refn_import
-typedef int(*gpr_unref_type)(gpr_refcount *r);
-extern gpr_unref_type gpr_unref_import;
-#define gpr_unref gpr_unref_import
-typedef void(*gpr_stats_init_type)(gpr_stats_counter *c, intptr_t n);
-extern gpr_stats_init_type gpr_stats_init_import;
-#define gpr_stats_init gpr_stats_init_import
-typedef void(*gpr_stats_inc_type)(gpr_stats_counter *c, intptr_t inc);
-extern gpr_stats_inc_type gpr_stats_inc_import;
-#define gpr_stats_inc gpr_stats_inc_import
-typedef intptr_t(*gpr_stats_read_type)(const gpr_stats_counter *c);
-extern gpr_stats_read_type gpr_stats_read_import;
-#define gpr_stats_read gpr_stats_read_import
-typedef gpr_timespec(*gpr_time_0_type)(gpr_clock_type type);
-extern gpr_time_0_type gpr_time_0_import;
-#define gpr_time_0 gpr_time_0_import
-typedef gpr_timespec(*gpr_inf_future_type)(gpr_clock_type type);
-extern gpr_inf_future_type gpr_inf_future_import;
-#define gpr_inf_future gpr_inf_future_import
-typedef gpr_timespec(*gpr_inf_past_type)(gpr_clock_type type);
-extern gpr_inf_past_type gpr_inf_past_import;
-#define gpr_inf_past gpr_inf_past_import
-typedef void(*gpr_time_init_type)(void);
-extern gpr_time_init_type gpr_time_init_import;
-#define gpr_time_init gpr_time_init_import
-typedef gpr_timespec(*gpr_now_type)(gpr_clock_type clock);
-extern gpr_now_type gpr_now_import;
-#define gpr_now gpr_now_import
-typedef gpr_timespec(*gpr_convert_clock_type_type)(gpr_timespec t, gpr_clock_type target_clock);
-extern gpr_convert_clock_type_type gpr_convert_clock_type_import;
-#define gpr_convert_clock_type gpr_convert_clock_type_import
-typedef int(*gpr_time_cmp_type)(gpr_timespec a, gpr_timespec b);
-extern gpr_time_cmp_type gpr_time_cmp_import;
-#define gpr_time_cmp gpr_time_cmp_import
-typedef gpr_timespec(*gpr_time_max_type)(gpr_timespec a, gpr_timespec b);
-extern gpr_time_max_type gpr_time_max_import;
-#define gpr_time_max gpr_time_max_import
-typedef gpr_timespec(*gpr_time_min_type)(gpr_timespec a, gpr_timespec b);
-extern gpr_time_min_type gpr_time_min_import;
-#define gpr_time_min gpr_time_min_import
-typedef gpr_timespec(*gpr_time_add_type)(gpr_timespec a, gpr_timespec b);
-extern gpr_time_add_type gpr_time_add_import;
-#define gpr_time_add gpr_time_add_import
-typedef gpr_timespec(*gpr_time_sub_type)(gpr_timespec a, gpr_timespec b);
-extern gpr_time_sub_type gpr_time_sub_import;
-#define gpr_time_sub gpr_time_sub_import
-typedef gpr_timespec(*gpr_time_from_micros_type)(int64_t x, gpr_clock_type clock_type);
-extern gpr_time_from_micros_type gpr_time_from_micros_import;
-#define gpr_time_from_micros gpr_time_from_micros_import
-typedef gpr_timespec(*gpr_time_from_nanos_type)(int64_t x, gpr_clock_type clock_type);
-extern gpr_time_from_nanos_type gpr_time_from_nanos_import;
-#define gpr_time_from_nanos gpr_time_from_nanos_import
-typedef gpr_timespec(*gpr_time_from_millis_type)(int64_t x, gpr_clock_type clock_type);
-extern gpr_time_from_millis_type gpr_time_from_millis_import;
-#define gpr_time_from_millis gpr_time_from_millis_import
-typedef gpr_timespec(*gpr_time_from_seconds_type)(int64_t x, gpr_clock_type clock_type);
-extern gpr_time_from_seconds_type gpr_time_from_seconds_import;
-#define gpr_time_from_seconds gpr_time_from_seconds_import
-typedef gpr_timespec(*gpr_time_from_minutes_type)(int64_t x, gpr_clock_type clock_type);
-extern gpr_time_from_minutes_type gpr_time_from_minutes_import;
-#define gpr_time_from_minutes gpr_time_from_minutes_import
-typedef gpr_timespec(*gpr_time_from_hours_type)(int64_t x, gpr_clock_type clock_type);
-extern gpr_time_from_hours_type gpr_time_from_hours_import;
-#define gpr_time_from_hours gpr_time_from_hours_import
-typedef int32_t(*gpr_time_to_millis_type)(gpr_timespec timespec);
-extern gpr_time_to_millis_type gpr_time_to_millis_import;
-#define gpr_time_to_millis gpr_time_to_millis_import
-typedef int(*gpr_time_similar_type)(gpr_timespec a, gpr_timespec b, gpr_timespec threshold);
-extern gpr_time_similar_type gpr_time_similar_import;
-#define gpr_time_similar gpr_time_similar_import
-typedef void(*gpr_sleep_until_type)(gpr_timespec until);
-extern gpr_sleep_until_type gpr_sleep_until_import;
-#define gpr_sleep_until gpr_sleep_until_import
-typedef double(*gpr_timespec_to_micros_type)(gpr_timespec t);
-extern gpr_timespec_to_micros_type gpr_timespec_to_micros_import;
-#define gpr_timespec_to_micros gpr_timespec_to_micros_import
 typedef gpr_avl(*gpr_avl_create_type)(const gpr_avl_vtable *vtable);
 extern gpr_avl_create_type gpr_avl_create_import;
 #define gpr_avl_create gpr_avl_create_import
@@ -827,9 +581,111 @@
 typedef int(*gpr_split_host_port_type)(const char *name, char **host, char **port);
 extern gpr_split_host_port_type gpr_split_host_port_import;
 #define gpr_split_host_port gpr_split_host_port_import
+typedef void(*gpr_log_type)(const char *file, int line, gpr_log_severity severity, const char *format, ...) GPRC_PRINT_FORMAT_CHECK(4, 5);
+extern gpr_log_type gpr_log_import;
+#define gpr_log gpr_log_import
+typedef void(*gpr_log_message_type)(const char *file, int line, gpr_log_severity severity, const char *message);
+extern gpr_log_message_type gpr_log_message_import;
+#define gpr_log_message gpr_log_message_import
+typedef void(*gpr_set_log_verbosity_type)(gpr_log_severity min_severity_to_print);
+extern gpr_set_log_verbosity_type gpr_set_log_verbosity_import;
+#define gpr_set_log_verbosity gpr_set_log_verbosity_import
+typedef void(*gpr_log_verbosity_init_type)();
+extern gpr_log_verbosity_init_type gpr_log_verbosity_init_import;
+#define gpr_log_verbosity_init gpr_log_verbosity_init_import
+typedef void(*gpr_set_log_function_type)(gpr_log_func func);
+extern gpr_set_log_function_type gpr_set_log_function_import;
+#define gpr_set_log_function gpr_set_log_function_import
 typedef char *(*gpr_format_message_type)(int messageid);
 extern gpr_format_message_type gpr_format_message_import;
 #define gpr_format_message gpr_format_message_import
+typedef gpr_slice(*gpr_slice_ref_type)(gpr_slice s);
+extern gpr_slice_ref_type gpr_slice_ref_import;
+#define gpr_slice_ref gpr_slice_ref_import
+typedef void(*gpr_slice_unref_type)(gpr_slice s);
+extern gpr_slice_unref_type gpr_slice_unref_import;
+#define gpr_slice_unref gpr_slice_unref_import
+typedef gpr_slice(*gpr_slice_new_type)(void *p, size_t len, void (*destroy)(void *));
+extern gpr_slice_new_type gpr_slice_new_import;
+#define gpr_slice_new gpr_slice_new_import
+typedef gpr_slice(*gpr_slice_new_with_user_data_type)(void *p, size_t len, void (*destroy)(void *), void *user_data);
+extern gpr_slice_new_with_user_data_type gpr_slice_new_with_user_data_import;
+#define gpr_slice_new_with_user_data gpr_slice_new_with_user_data_import
+typedef gpr_slice(*gpr_slice_new_with_len_type)(void *p, size_t len, void (*destroy)(void *, size_t));
+extern gpr_slice_new_with_len_type gpr_slice_new_with_len_import;
+#define gpr_slice_new_with_len gpr_slice_new_with_len_import
+typedef gpr_slice(*gpr_slice_malloc_type)(size_t length);
+extern gpr_slice_malloc_type gpr_slice_malloc_import;
+#define gpr_slice_malloc gpr_slice_malloc_import
+typedef gpr_slice(*gpr_slice_from_copied_string_type)(const char *source);
+extern gpr_slice_from_copied_string_type gpr_slice_from_copied_string_import;
+#define gpr_slice_from_copied_string gpr_slice_from_copied_string_import
+typedef gpr_slice(*gpr_slice_from_copied_buffer_type)(const char *source, size_t len);
+extern gpr_slice_from_copied_buffer_type gpr_slice_from_copied_buffer_import;
+#define gpr_slice_from_copied_buffer gpr_slice_from_copied_buffer_import
+typedef gpr_slice(*gpr_slice_from_static_string_type)(const char *source);
+extern gpr_slice_from_static_string_type gpr_slice_from_static_string_import;
+#define gpr_slice_from_static_string gpr_slice_from_static_string_import
+typedef gpr_slice(*gpr_slice_sub_type)(gpr_slice s, size_t begin, size_t end);
+extern gpr_slice_sub_type gpr_slice_sub_import;
+#define gpr_slice_sub gpr_slice_sub_import
+typedef gpr_slice(*gpr_slice_sub_no_ref_type)(gpr_slice s, size_t begin, size_t end);
+extern gpr_slice_sub_no_ref_type gpr_slice_sub_no_ref_import;
+#define gpr_slice_sub_no_ref gpr_slice_sub_no_ref_import
+typedef gpr_slice(*gpr_slice_split_tail_type)(gpr_slice *s, size_t split);
+extern gpr_slice_split_tail_type gpr_slice_split_tail_import;
+#define gpr_slice_split_tail gpr_slice_split_tail_import
+typedef gpr_slice(*gpr_slice_split_head_type)(gpr_slice *s, size_t split);
+extern gpr_slice_split_head_type gpr_slice_split_head_import;
+#define gpr_slice_split_head gpr_slice_split_head_import
+typedef gpr_slice(*gpr_empty_slice_type)(void);
+extern gpr_empty_slice_type gpr_empty_slice_import;
+#define gpr_empty_slice gpr_empty_slice_import
+typedef int(*gpr_slice_cmp_type)(gpr_slice a, gpr_slice b);
+extern gpr_slice_cmp_type gpr_slice_cmp_import;
+#define gpr_slice_cmp gpr_slice_cmp_import
+typedef int(*gpr_slice_str_cmp_type)(gpr_slice a, const char *b);
+extern gpr_slice_str_cmp_type gpr_slice_str_cmp_import;
+#define gpr_slice_str_cmp gpr_slice_str_cmp_import
+typedef void(*gpr_slice_buffer_init_type)(gpr_slice_buffer *sb);
+extern gpr_slice_buffer_init_type gpr_slice_buffer_init_import;
+#define gpr_slice_buffer_init gpr_slice_buffer_init_import
+typedef void(*gpr_slice_buffer_destroy_type)(gpr_slice_buffer *sb);
+extern gpr_slice_buffer_destroy_type gpr_slice_buffer_destroy_import;
+#define gpr_slice_buffer_destroy gpr_slice_buffer_destroy_import
+typedef void(*gpr_slice_buffer_add_type)(gpr_slice_buffer *sb, gpr_slice slice);
+extern gpr_slice_buffer_add_type gpr_slice_buffer_add_import;
+#define gpr_slice_buffer_add gpr_slice_buffer_add_import
+typedef size_t(*gpr_slice_buffer_add_indexed_type)(gpr_slice_buffer *sb, gpr_slice slice);
+extern gpr_slice_buffer_add_indexed_type gpr_slice_buffer_add_indexed_import;
+#define gpr_slice_buffer_add_indexed gpr_slice_buffer_add_indexed_import
+typedef void(*gpr_slice_buffer_addn_type)(gpr_slice_buffer *sb, gpr_slice *slices, size_t n);
+extern gpr_slice_buffer_addn_type gpr_slice_buffer_addn_import;
+#define gpr_slice_buffer_addn gpr_slice_buffer_addn_import
+typedef uint8_t *(*gpr_slice_buffer_tiny_add_type)(gpr_slice_buffer *sb, size_t len);
+extern gpr_slice_buffer_tiny_add_type gpr_slice_buffer_tiny_add_import;
+#define gpr_slice_buffer_tiny_add gpr_slice_buffer_tiny_add_import
+typedef void(*gpr_slice_buffer_pop_type)(gpr_slice_buffer *sb);
+extern gpr_slice_buffer_pop_type gpr_slice_buffer_pop_import;
+#define gpr_slice_buffer_pop gpr_slice_buffer_pop_import
+typedef void(*gpr_slice_buffer_reset_and_unref_type)(gpr_slice_buffer *sb);
+extern gpr_slice_buffer_reset_and_unref_type gpr_slice_buffer_reset_and_unref_import;
+#define gpr_slice_buffer_reset_and_unref gpr_slice_buffer_reset_and_unref_import
+typedef void(*gpr_slice_buffer_swap_type)(gpr_slice_buffer *a, gpr_slice_buffer *b);
+extern gpr_slice_buffer_swap_type gpr_slice_buffer_swap_import;
+#define gpr_slice_buffer_swap gpr_slice_buffer_swap_import
+typedef void(*gpr_slice_buffer_move_into_type)(gpr_slice_buffer *src, gpr_slice_buffer *dst);
+extern gpr_slice_buffer_move_into_type gpr_slice_buffer_move_into_import;
+#define gpr_slice_buffer_move_into gpr_slice_buffer_move_into_import
+typedef void(*gpr_slice_buffer_trim_end_type)(gpr_slice_buffer *src, size_t n, gpr_slice_buffer *garbage);
+extern gpr_slice_buffer_trim_end_type gpr_slice_buffer_trim_end_import;
+#define gpr_slice_buffer_trim_end gpr_slice_buffer_trim_end_import
+typedef void(*gpr_slice_buffer_move_first_type)(gpr_slice_buffer *src, size_t n, gpr_slice_buffer *dst);
+extern gpr_slice_buffer_move_first_type gpr_slice_buffer_move_first_import;
+#define gpr_slice_buffer_move_first gpr_slice_buffer_move_first_import
+typedef gpr_slice(*gpr_slice_buffer_take_first_type)(gpr_slice_buffer *src);
+extern gpr_slice_buffer_take_first_type gpr_slice_buffer_take_first_import;
+#define gpr_slice_buffer_take_first gpr_slice_buffer_take_first_import
 typedef char *(*gpr_strdup_type)(const char *src);
 extern gpr_strdup_type gpr_strdup_import;
 #define gpr_strdup gpr_strdup_import
@@ -851,6 +707,75 @@
 typedef void(*gpr_subprocess_interrupt_type)(gpr_subprocess *p);
 extern gpr_subprocess_interrupt_type gpr_subprocess_interrupt_import;
 #define gpr_subprocess_interrupt gpr_subprocess_interrupt_import
+typedef void(*gpr_mu_init_type)(gpr_mu *mu);
+extern gpr_mu_init_type gpr_mu_init_import;
+#define gpr_mu_init gpr_mu_init_import
+typedef void(*gpr_mu_destroy_type)(gpr_mu *mu);
+extern gpr_mu_destroy_type gpr_mu_destroy_import;
+#define gpr_mu_destroy gpr_mu_destroy_import
+typedef void(*gpr_mu_lock_type)(gpr_mu *mu);
+extern gpr_mu_lock_type gpr_mu_lock_import;
+#define gpr_mu_lock gpr_mu_lock_import
+typedef void(*gpr_mu_unlock_type)(gpr_mu *mu);
+extern gpr_mu_unlock_type gpr_mu_unlock_import;
+#define gpr_mu_unlock gpr_mu_unlock_import
+typedef int(*gpr_mu_trylock_type)(gpr_mu *mu);
+extern gpr_mu_trylock_type gpr_mu_trylock_import;
+#define gpr_mu_trylock gpr_mu_trylock_import
+typedef void(*gpr_cv_init_type)(gpr_cv *cv);
+extern gpr_cv_init_type gpr_cv_init_import;
+#define gpr_cv_init gpr_cv_init_import
+typedef void(*gpr_cv_destroy_type)(gpr_cv *cv);
+extern gpr_cv_destroy_type gpr_cv_destroy_import;
+#define gpr_cv_destroy gpr_cv_destroy_import
+typedef int(*gpr_cv_wait_type)(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline);
+extern gpr_cv_wait_type gpr_cv_wait_import;
+#define gpr_cv_wait gpr_cv_wait_import
+typedef void(*gpr_cv_signal_type)(gpr_cv *cv);
+extern gpr_cv_signal_type gpr_cv_signal_import;
+#define gpr_cv_signal gpr_cv_signal_import
+typedef void(*gpr_cv_broadcast_type)(gpr_cv *cv);
+extern gpr_cv_broadcast_type gpr_cv_broadcast_import;
+#define gpr_cv_broadcast gpr_cv_broadcast_import
+typedef void(*gpr_once_init_type)(gpr_once *once, void (*init_routine)(void));
+extern gpr_once_init_type gpr_once_init_import;
+#define gpr_once_init gpr_once_init_import
+typedef void(*gpr_event_init_type)(gpr_event *ev);
+extern gpr_event_init_type gpr_event_init_import;
+#define gpr_event_init gpr_event_init_import
+typedef void(*gpr_event_set_type)(gpr_event *ev, void *value);
+extern gpr_event_set_type gpr_event_set_import;
+#define gpr_event_set gpr_event_set_import
+typedef void *(*gpr_event_get_type)(gpr_event *ev);
+extern gpr_event_get_type gpr_event_get_import;
+#define gpr_event_get gpr_event_get_import
+typedef void *(*gpr_event_wait_type)(gpr_event *ev, gpr_timespec abs_deadline);
+extern gpr_event_wait_type gpr_event_wait_import;
+#define gpr_event_wait gpr_event_wait_import
+typedef void(*gpr_ref_init_type)(gpr_refcount *r, int n);
+extern gpr_ref_init_type gpr_ref_init_import;
+#define gpr_ref_init gpr_ref_init_import
+typedef void(*gpr_ref_type)(gpr_refcount *r);
+extern gpr_ref_type gpr_ref_import;
+#define gpr_ref gpr_ref_import
+typedef void(*gpr_ref_non_zero_type)(gpr_refcount *r);
+extern gpr_ref_non_zero_type gpr_ref_non_zero_import;
+#define gpr_ref_non_zero gpr_ref_non_zero_import
+typedef void(*gpr_refn_type)(gpr_refcount *r, int n);
+extern gpr_refn_type gpr_refn_import;
+#define gpr_refn gpr_refn_import
+typedef int(*gpr_unref_type)(gpr_refcount *r);
+extern gpr_unref_type gpr_unref_import;
+#define gpr_unref gpr_unref_import
+typedef void(*gpr_stats_init_type)(gpr_stats_counter *c, intptr_t n);
+extern gpr_stats_init_type gpr_stats_init_import;
+#define gpr_stats_init gpr_stats_init_import
+typedef void(*gpr_stats_inc_type)(gpr_stats_counter *c, intptr_t inc);
+extern gpr_stats_inc_type gpr_stats_inc_import;
+#define gpr_stats_inc gpr_stats_inc_import
+typedef intptr_t(*gpr_stats_read_type)(const gpr_stats_counter *c);
+extern gpr_stats_read_type gpr_stats_read_import;
+#define gpr_stats_read gpr_stats_read_import
 typedef int(*gpr_thd_new_type)(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg, const gpr_thd_options *options);
 extern gpr_thd_new_type gpr_thd_new_import;
 #define gpr_thd_new gpr_thd_new_import
@@ -875,6 +800,69 @@
 typedef void(*gpr_thd_join_type)(gpr_thd_id t);
 extern gpr_thd_join_type gpr_thd_join_import;
 #define gpr_thd_join gpr_thd_join_import
+typedef gpr_timespec(*gpr_time_0_type)(gpr_clock_type type);
+extern gpr_time_0_type gpr_time_0_import;
+#define gpr_time_0 gpr_time_0_import
+typedef gpr_timespec(*gpr_inf_future_type)(gpr_clock_type type);
+extern gpr_inf_future_type gpr_inf_future_import;
+#define gpr_inf_future gpr_inf_future_import
+typedef gpr_timespec(*gpr_inf_past_type)(gpr_clock_type type);
+extern gpr_inf_past_type gpr_inf_past_import;
+#define gpr_inf_past gpr_inf_past_import
+typedef void(*gpr_time_init_type)(void);
+extern gpr_time_init_type gpr_time_init_import;
+#define gpr_time_init gpr_time_init_import
+typedef gpr_timespec(*gpr_now_type)(gpr_clock_type clock);
+extern gpr_now_type gpr_now_import;
+#define gpr_now gpr_now_import
+typedef gpr_timespec(*gpr_convert_clock_type_type)(gpr_timespec t, gpr_clock_type target_clock);
+extern gpr_convert_clock_type_type gpr_convert_clock_type_import;
+#define gpr_convert_clock_type gpr_convert_clock_type_import
+typedef int(*gpr_time_cmp_type)(gpr_timespec a, gpr_timespec b);
+extern gpr_time_cmp_type gpr_time_cmp_import;
+#define gpr_time_cmp gpr_time_cmp_import
+typedef gpr_timespec(*gpr_time_max_type)(gpr_timespec a, gpr_timespec b);
+extern gpr_time_max_type gpr_time_max_import;
+#define gpr_time_max gpr_time_max_import
+typedef gpr_timespec(*gpr_time_min_type)(gpr_timespec a, gpr_timespec b);
+extern gpr_time_min_type gpr_time_min_import;
+#define gpr_time_min gpr_time_min_import
+typedef gpr_timespec(*gpr_time_add_type)(gpr_timespec a, gpr_timespec b);
+extern gpr_time_add_type gpr_time_add_import;
+#define gpr_time_add gpr_time_add_import
+typedef gpr_timespec(*gpr_time_sub_type)(gpr_timespec a, gpr_timespec b);
+extern gpr_time_sub_type gpr_time_sub_import;
+#define gpr_time_sub gpr_time_sub_import
+typedef gpr_timespec(*gpr_time_from_micros_type)(int64_t x, gpr_clock_type clock_type);
+extern gpr_time_from_micros_type gpr_time_from_micros_import;
+#define gpr_time_from_micros gpr_time_from_micros_import
+typedef gpr_timespec(*gpr_time_from_nanos_type)(int64_t x, gpr_clock_type clock_type);
+extern gpr_time_from_nanos_type gpr_time_from_nanos_import;
+#define gpr_time_from_nanos gpr_time_from_nanos_import
+typedef gpr_timespec(*gpr_time_from_millis_type)(int64_t x, gpr_clock_type clock_type);
+extern gpr_time_from_millis_type gpr_time_from_millis_import;
+#define gpr_time_from_millis gpr_time_from_millis_import
+typedef gpr_timespec(*gpr_time_from_seconds_type)(int64_t x, gpr_clock_type clock_type);
+extern gpr_time_from_seconds_type gpr_time_from_seconds_import;
+#define gpr_time_from_seconds gpr_time_from_seconds_import
+typedef gpr_timespec(*gpr_time_from_minutes_type)(int64_t x, gpr_clock_type clock_type);
+extern gpr_time_from_minutes_type gpr_time_from_minutes_import;
+#define gpr_time_from_minutes gpr_time_from_minutes_import
+typedef gpr_timespec(*gpr_time_from_hours_type)(int64_t x, gpr_clock_type clock_type);
+extern gpr_time_from_hours_type gpr_time_from_hours_import;
+#define gpr_time_from_hours gpr_time_from_hours_import
+typedef int32_t(*gpr_time_to_millis_type)(gpr_timespec timespec);
+extern gpr_time_to_millis_type gpr_time_to_millis_import;
+#define gpr_time_to_millis gpr_time_to_millis_import
+typedef int(*gpr_time_similar_type)(gpr_timespec a, gpr_timespec b, gpr_timespec threshold);
+extern gpr_time_similar_type gpr_time_similar_import;
+#define gpr_time_similar gpr_time_similar_import
+typedef void(*gpr_sleep_until_type)(gpr_timespec until);
+extern gpr_sleep_until_type gpr_sleep_until_import;
+#define gpr_sleep_until gpr_sleep_until_import
+typedef double(*gpr_timespec_to_micros_type)(gpr_timespec t);
+extern gpr_timespec_to_micros_type gpr_timespec_to_micros_import;
+#define gpr_timespec_to_micros gpr_timespec_to_micros_import
 
 void grpc_rb_load_imports(HMODULE library);
 
diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb
index 4260d85..dfc2644 100644
--- a/src/ruby/lib/grpc/generic/active_call.rb
+++ b/src/ruby/lib/grpc/generic/active_call.rb
@@ -58,7 +58,7 @@
     include Core::TimeConsts
     include Core::CallOps
     extend Forwardable
-    attr_reader(:deadline)
+    attr_reader :deadline, :metadata_sent, :metadata_to_send
     def_delegators :@call, :cancel, :metadata, :write_flag, :write_flag=,
                    :peer, :peer_cert, :trailing_metadata
 
@@ -101,7 +101,7 @@
     # @param metadata_received [true|false] indicates if metadata has already
     #     been received. Should always be true for server calls
     def initialize(call, marshal, unmarshal, deadline, started: true,
-                   metadata_received: false)
+                   metadata_received: false, metadata_to_send: nil)
       fail(TypeError, '!Core::Call') unless call.is_a? Core::Call
       @call = call
       @deadline = deadline
@@ -110,6 +110,20 @@
       @metadata_received = metadata_received
       @metadata_sent = started
       @op_notifier = nil
+
+      fail(ArgumentError, 'Already sent md') if started && metadata_to_send
+      @metadata_to_send = metadata_to_send || {} unless started
+      @send_initial_md_mutex = Mutex.new
+    end
+
+    # Sends the initial metadata that has yet to be sent.
+    # Does nothing if metadata has already been sent for this call.
+    def send_initial_metadata
+      @send_initial_md_mutex.synchronize do
+        return if @metadata_sent
+        @metadata_tag = ActiveCall.client_invoke(@call, @metadata_to_send)
+        @metadata_sent = true
+      end
     end
 
     # output_metadata are provides access to hash that can be used to
@@ -187,7 +201,7 @@
     # @param marshalled [false, true] indicates if the object is already
     # marshalled.
     def remote_send(req, marshalled = false)
-      # TODO(murgatroid99): ensure metadata was sent
+      send_initial_metadata
       GRPC.logger.debug("sending #{req}, marshalled? #{marshalled}")
       payload = marshalled ? req : @marshal.call(req)
       @call.run_batch(SEND_MESSAGE => payload)
@@ -203,6 +217,7 @@
     # list, mulitple metadata for its key are sent
     def send_status(code = OK, details = '', assert_finished = false,
                     metadata: {})
+      send_initial_metadata
       ops = {
         SEND_STATUS_FROM_SERVER => Struct::Status.new(code, details, metadata)
       }
@@ -225,11 +240,8 @@
         @call.metadata = batch_result.metadata
         @metadata_received = true
       end
-      GRPC.logger.debug("received req: #{batch_result}")
       unless batch_result.nil? || batch_result.message.nil?
-        GRPC.logger.debug("received req.to_s: #{batch_result.message}")
         res = @unmarshal.call(batch_result.message)
-        GRPC.logger.debug("received_req (unmarshalled): #{res.inspect}")
         return res
       end
       GRPC.logger.debug('found nil; the final response has been sent')
@@ -303,7 +315,7 @@
     # a list, multiple metadata for its key are sent
     # @return [Object] the response received from the server
     def request_response(req, metadata: {})
-      start_call(metadata)
+      merge_metadata_to_send(metadata) && send_initial_metadata
       remote_send(req)
       writes_done(false)
       response = remote_read
@@ -327,7 +339,7 @@
     # a list, multiple metadata for its key are sent
     # @return [Object] the response received from the server
     def client_streamer(requests, metadata: {})
-      start_call(metadata)
+      merge_metadata_to_send(metadata) && send_initial_metadata
       requests.each { |r| remote_send(r) }
       writes_done(false)
       response = remote_read
@@ -353,7 +365,7 @@
     # a list, multiple metadata for its key are sent
     # @return [Enumerator|nil] a response Enumerator
     def server_streamer(req, metadata: {})
-      start_call(metadata)
+      merge_metadata_to_send(metadata) && send_initial_metadata
       remote_send(req)
       writes_done(false)
       replies = enum_for(:each_remote_read_then_finish)
@@ -392,9 +404,12 @@
     # a list, multiple metadata for its key are sent
     # @return [Enumerator, nil] a response Enumerator
     def bidi_streamer(requests, metadata: {}, &blk)
-      start_call(metadata)
-      bd = BidiCall.new(@call, @marshal, @unmarshal,
+      merge_metadata_to_send(metadata) && send_initial_metadata
+      bd = BidiCall.new(@call,
+                        @marshal,
+                        @unmarshal,
                         metadata_received: @metadata_received)
+
       bd.run_on_client(requests, @op_notifier, &blk)
     end
 
@@ -410,8 +425,12 @@
     #
     # @param gen_each_reply [Proc] generates the BiDi stream replies
     def run_server_bidi(gen_each_reply)
-      bd = BidiCall.new(@call, @marshal, @unmarshal,
-                        metadata_received: @metadata_received)
+      bd = BidiCall.new(@call,
+                        @marshal,
+                        @unmarshal,
+                        metadata_received: @metadata_received,
+                        req_view: MultiReqView.new(self))
+
       bd.run_on_server(gen_each_reply)
     end
 
@@ -428,15 +447,23 @@
       @op_notifier.notify(self)
     end
 
+    # Add to the metadata that will be sent from the server.
+    # Fails if metadata has already been sent.
+    # Unused by client calls.
+    def merge_metadata_to_send(new_metadata = {})
+      @send_initial_md_mutex.synchronize do
+        fail('cant change metadata after already sent') if @metadata_sent
+        @metadata_to_send.merge!(new_metadata)
+      end
+    end
+
     private
 
     # Starts the call if not already started
     # @param metadata [Hash] metadata to be sent to the server. If a value is
     # a list, multiple metadata for its key are sent
     def start_call(metadata = {})
-      return if @metadata_sent
-      @metadata_tag = ActiveCall.client_invoke(@call, metadata)
-      @metadata_sent = true
+      merge_metadata_to_send(metadata) && send_initial_metadata
     end
 
     def self.view_class(*visible_methods)
@@ -454,12 +481,20 @@
     # SingleReqView limits access to an ActiveCall's methods for use in server
     # handlers that receive just one request.
     SingleReqView = view_class(:cancelled?, :deadline, :metadata,
-                               :output_metadata, :peer, :peer_cert)
+                               :output_metadata, :peer, :peer_cert,
+                               :send_initial_metadata,
+                               :metadata_to_send,
+                               :merge_metadata_to_send,
+                               :metadata_sent)
 
     # MultiReqView limits access to an ActiveCall's methods for use in
     # server client_streamer handlers.
     MultiReqView = view_class(:cancelled?, :deadline, :each_queued_msg,
-                              :each_remote_read, :metadata, :output_metadata)
+                              :each_remote_read, :metadata, :output_metadata,
+                              :send_initial_metadata,
+                              :metadata_to_send,
+                              :merge_metadata_to_send,
+                              :metadata_sent)
 
     # Operation limits access to an ActiveCall's methods for use as
     # a Operation on the client.
diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb
index c2ac3c4..d7cd9e6 100644
--- a/src/ruby/lib/grpc/generic/bidi_call.rb
+++ b/src/ruby/lib/grpc/generic/bidi_call.rb
@@ -56,18 +56,19 @@
     # @param unmarshal [Function] f(string)->obj that unmarshals responses
     # @param metadata_received [true|false] indicates if metadata has already
     #     been received. Should always be true for server calls
-    def initialize(call, marshal, unmarshal, metadata_received: false)
+    def initialize(call, marshal, unmarshal, metadata_received: false,
+                   req_view: nil)
       fail(ArgumentError, 'not a call') unless call.is_a? Core::Call
       @call = call
       @marshal = marshal
       @op_notifier = nil  # signals completion on clients
-      @readq = Queue.new
       @unmarshal = unmarshal
       @metadata_received = metadata_received
       @reads_complete = false
       @writes_complete = false
       @complete = false
       @done_mutex = Mutex.new
+      @req_view = req_view
     end
 
     # Begins orchestration of the Bidi stream for a client sending requests.
@@ -81,8 +82,7 @@
     def run_on_client(requests, op_notifier, &blk)
       @op_notifier = op_notifier
       @enq_th = Thread.new { write_loop(requests) }
-      @loop_th = start_read_loop
-      each_queued_msg(&blk)
+      read_loop(&blk)
     end
 
     # Begins orchestration of the Bidi stream for a server generating replies.
@@ -97,8 +97,15 @@
     #
     # @param gen_each_reply [Proc] generates the BiDi stream replies.
     def run_on_server(gen_each_reply)
-      replys = gen_each_reply.call(each_queued_msg)
-      @loop_th = start_read_loop(is_client: false)
+      # Pass in the optional call object parameter if possible
+      if gen_each_reply.arity == 1
+        replys = gen_each_reply.call(read_loop(is_client: false))
+      elsif gen_each_reply.arity == 2
+        replys = gen_each_reply.call(read_loop(is_client: false), @req_view)
+      else
+        fail 'Illegal arity of reply generator'
+      end
+
       write_loop(replys, is_client: false)
     end
 
@@ -135,24 +142,6 @@
       batch_result
     end
 
-    # each_queued_msg yields each message on this instances readq
-    #
-    # - messages are added to the readq by #read_loop
-    # - iteration ends when the instance itself is added
-    def each_queued_msg
-      return enum_for(:each_queued_msg) unless block_given?
-      count = 0
-      loop do
-        GRPC.logger.debug("each_queued_msg: waiting##{count}")
-        count += 1
-        req = @readq.pop
-        GRPC.logger.debug("each_queued_msg: req = #{req}")
-        fail req if req.is_a? StandardError
-        break if req.equal?(END_OF_READS)
-        yield req
-      end
-    end
-
     def write_loop(requests, is_client: true)
       GRPC.logger.debug('bidi-write-loop: starting')
       count = 0
@@ -162,6 +151,7 @@
         payload = @marshal.call(req)
         # Fails if status already received
         begin
+          @req_view.send_initial_metadata unless @req_view.nil?
           @call.run_batch(SEND_MESSAGE => payload)
         rescue GRPC::Core::CallError => e
           # This is almost definitely caused by a status arriving while still
@@ -190,47 +180,45 @@
       raise e
     end
 
-    # starts the read loop
-    def start_read_loop(is_client: true)
-      Thread.new do
-        GRPC.logger.debug('bidi-read-loop: starting')
-        begin
-          count = 0
-          # queue the initial read before beginning the loop
-          loop do
-            GRPC.logger.debug("bidi-read-loop: #{count}")
-            count += 1
-            batch_result = read_using_run_batch
+    # Provides an enumerator that yields results of remote reads
+    def read_loop(is_client: true)
+      return enum_for(:read_loop,
+                      is_client: is_client) unless block_given?
+      GRPC.logger.debug('bidi-read-loop: starting')
+      begin
+        count = 0
+        # queue the initial read before beginning the loop
+        loop do
+          GRPC.logger.debug("bidi-read-loop: #{count}")
+          count += 1
+          batch_result = read_using_run_batch
 
-            # handle the next message
-            if batch_result.message.nil?
-              GRPC.logger.debug("bidi-read-loop: null batch #{batch_result}")
+          # handle the next message
+          if batch_result.message.nil?
+            GRPC.logger.debug("bidi-read-loop: null batch #{batch_result}")
 
-              if is_client
-                batch_result = @call.run_batch(RECV_STATUS_ON_CLIENT => nil)
-                @call.status = batch_result.status
-                batch_result.check_status
-                GRPC.logger.debug("bidi-read-loop: done status #{@call.status}")
-              end
-
-              @readq.push(END_OF_READS)
-              GRPC.logger.debug('bidi-read-loop: done reading!')
-              break
+            if is_client
+              batch_result = @call.run_batch(RECV_STATUS_ON_CLIENT => nil)
+              @call.status = batch_result.status
+              batch_result.check_status
+              GRPC.logger.debug("bidi-read-loop: done status #{@call.status}")
             end
 
-            # push the latest read onto the queue and continue reading
-            res = @unmarshal.call(batch_result.message)
-            @readq.push(res)
+            GRPC.logger.debug('bidi-read-loop: done reading!')
+            break
           end
-        rescue StandardError => e
-          GRPC.logger.warn('bidi: read-loop failed')
-          GRPC.logger.warn(e)
-          @readq.push(e)  # let each_queued_msg terminate with this error
+
+          res = @unmarshal.call(batch_result.message)
+          yield res
         end
-        GRPC.logger.debug('bidi-read-loop: finished')
-        @reads_complete = true
-        finished
+      rescue StandardError => e
+        GRPC.logger.warn('bidi: read-loop failed')
+        GRPC.logger.warn(e)
+        raise e
       end
+      GRPC.logger.debug('bidi-read-loop: finished')
+      @reads_complete = true
+      finished
     end
   end
 end
diff --git a/src/ruby/lib/grpc/generic/rpc_desc.rb b/src/ruby/lib/grpc/generic/rpc_desc.rb
index 913f55d..584fe78 100644
--- a/src/ruby/lib/grpc/generic/rpc_desc.rb
+++ b/src/ruby/lib/grpc/generic/rpc_desc.rb
@@ -104,7 +104,14 @@
     end
 
     def assert_arity_matches(mth)
-      if request_response? || server_streamer?
+      # A bidi handler function can optionally be passed a second
+      # call object parameter for access to metadata, cancelling, etc.
+      if bidi_streamer?
+        if mth.arity != 2 && mth.arity != 1
+          fail arity_error(mth, 2, "should be #{mth.name}(req, call) or " \
+            "#{mth.name}(req)")
+        end
+      elsif request_response? || server_streamer?
         if mth.arity != 2
           fail arity_error(mth, 2, "should be #{mth.name}(req, call)")
         end
diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb
index 7ea2371..7dbcb7d 100644
--- a/src/ruby/lib/grpc/generic/rpc_server.rb
+++ b/src/ruby/lib/grpc/generic/rpc_server.rb
@@ -31,113 +31,10 @@
 require_relative 'active_call'
 require_relative 'service'
 require 'thread'
+require 'concurrent'
 
 # GRPC contains the General RPC module.
 module GRPC
-  # Pool is a simple thread pool.
-  class Pool
-    # Default keep alive period is 1s
-    DEFAULT_KEEP_ALIVE = 1
-
-    def initialize(size, keep_alive: DEFAULT_KEEP_ALIVE)
-      fail 'pool size must be positive' unless size > 0
-      @jobs = Queue.new
-      @size = size
-      @stopped = false
-      @stop_mutex = Mutex.new # needs to be held when accessing @stopped
-      @stop_cond = ConditionVariable.new
-      @workers = []
-      @keep_alive = keep_alive
-    end
-
-    # Returns the number of jobs waiting
-    def jobs_waiting
-      @jobs.size
-    end
-
-    # Runs the given block on the queue with the provided args.
-    #
-    # @param args the args passed blk when it is called
-    # @param blk the block to call
-    def schedule(*args, &blk)
-      return if blk.nil?
-      @stop_mutex.synchronize do
-        if @stopped
-          GRPC.logger.warn('did not schedule job, already stopped')
-          return
-        end
-        GRPC.logger.info('schedule another job')
-        @jobs << [blk, args]
-      end
-    end
-
-    # Starts running the jobs in the thread pool.
-    def start
-      @stop_mutex.synchronize do
-        fail 'already stopped' if @stopped
-      end
-      until @workers.size == @size.to_i
-        next_thread = Thread.new do
-          catch(:exit) do  # allows { throw :exit } to kill a thread
-            loop_execute_jobs
-          end
-          remove_current_thread
-        end
-        @workers << next_thread
-      end
-    end
-
-    # Stops the jobs in the pool
-    def stop
-      GRPC.logger.info('stopping, will wait for all the workers to exit')
-      @workers.size.times { schedule { throw :exit } }
-      @stop_mutex.synchronize do  # wait @keep_alive for works to stop
-        @stopped = true
-        @stop_cond.wait(@stop_mutex, @keep_alive) if @workers.size > 0
-      end
-      forcibly_stop_workers
-      GRPC.logger.info('stopped, all workers are shutdown')
-    end
-
-    protected
-
-    # Forcibly shutdown any threads that are still alive.
-    def forcibly_stop_workers
-      return unless @workers.size > 0
-      GRPC.logger.info("forcibly terminating #{@workers.size} worker(s)")
-      @workers.each do |t|
-        next unless t.alive?
-        begin
-          t.exit
-        rescue StandardError => e
-          GRPC.logger.warn('error while terminating a worker')
-          GRPC.logger.warn(e)
-        end
-      end
-    end
-
-    # removes the threads from workers, and signal when all the
-    # threads are complete.
-    def remove_current_thread
-      @stop_mutex.synchronize do
-        @workers.delete(Thread.current)
-        @stop_cond.signal if @workers.size.zero?
-      end
-    end
-
-    def loop_execute_jobs
-      loop do
-        begin
-          blk, args = @jobs.pop
-          blk.call(*args)
-        rescue StandardError => e
-          GRPC.logger.warn('Error in worker thread')
-          GRPC.logger.warn(e)
-        end
-      end
-    end
-  end
-
   # RpcServer hosts a number of services and makes them available on the
   # network.
   class RpcServer
@@ -147,11 +44,14 @@
 
     def_delegators :@server, :add_http2_port
 
-    # Default thread pool size is 3
-    DEFAULT_POOL_SIZE = 3
+    # Default max size of the thread pool size is 100
+    DEFAULT_MAX_POOL_SIZE = 100
 
-    # Default max_waiting_requests size is 20
-    DEFAULT_MAX_WAITING_REQUESTS = 20
+    # Default minimum size of the thread pool is 5
+    DEFAULT_MIN_POOL_SIZE = 5
+
+    # Default max_waiting_requests size is 60
+    DEFAULT_MAX_WAITING_REQUESTS = 60
 
     # Default poll period is 1s
     DEFAULT_POLL_PERIOD = 1
@@ -174,8 +74,8 @@
     # There are some specific keyword args used to configure the RpcServer
     # instance.
     #
-    # * pool_size: the size of the thread pool the server uses to run its
-    # threads
+    # * pool_size: the maximum size of the thread pool that the server's
+    # thread pool can reach.
     #
     # * max_waiting_requests: the maximum number of requests that are not
     # being handled to allow. When this limit is exceeded, the server responds
@@ -191,7 +91,8 @@
     #
     # * server_args:
     # A server arguments hash to be passed down to the underlying core server
-    def initialize(pool_size:DEFAULT_POOL_SIZE,
+    def initialize(pool_size:DEFAULT_MAX_POOL_SIZE,
+                   min_pool_size:DEFAULT_MIN_POOL_SIZE,
                    max_waiting_requests:DEFAULT_MAX_WAITING_REQUESTS,
                    poll_period:DEFAULT_POLL_PERIOD,
                    connect_md_proc:nil,
@@ -199,8 +100,12 @@
       @connect_md_proc = RpcServer.setup_connect_md_proc(connect_md_proc)
       @max_waiting_requests = max_waiting_requests
       @poll_period = poll_period
-      @pool_size = pool_size
-      @pool = Pool.new(@pool_size)
+
+      @pool = Concurrent::ThreadPoolExecutor.new(
+        min_threads: [min_pool_size, pool_size].min,
+        max_threads: pool_size,
+        max_queue: max_waiting_requests,
+        fallback_policy: :discard)
       @run_cond = ConditionVariable.new
       @run_mutex = Mutex.new
       # running_state can take 4 values: :not_started, :running, :stopping, and
@@ -221,7 +126,8 @@
       end
       deadline = from_relative_time(@poll_period)
       @server.close(deadline)
-      @pool.stop
+      @pool.shutdown
+      @pool.wait_for_termination
     end
 
     def running_state
@@ -318,7 +224,6 @@
     def run
       @run_mutex.synchronize do
         fail 'cannot run without registering services' if rpc_descs.size.zero?
-        @pool.start
         @server.start
         transition_running_state(:running)
         @run_cond.broadcast
@@ -330,13 +235,18 @@
 
     # Sends RESOURCE_EXHAUSTED if there are too many unprocessed jobs
     def available?(an_rpc)
-      jobs_count, max = @pool.jobs_waiting, @max_waiting_requests
+      jobs_count, max = @pool.queue_length, @pool.max_queue
       GRPC.logger.info("waiting: #{jobs_count}, max: #{max}")
-      return an_rpc if @pool.jobs_waiting <= @max_waiting_requests
+
+      # remaining capacity for ThreadPoolExecutors is -1 if unbounded
+      return an_rpc if @pool.remaining_capacity != 0
       GRPC.logger.warn("NOT AVAILABLE: too many jobs_waiting: #{an_rpc}")
       noop = proc { |x| x }
+
+      # Create a new active call that knows that metadata hasn't been
+      # sent yet
       c = ActiveCall.new(an_rpc.call, noop, noop, an_rpc.deadline,
-                         metadata_received: true)
+                         metadata_received: true, started: false)
       c.send_status(GRPC::Core::StatusCodes::RESOURCE_EXHAUSTED, '')
       nil
     end
@@ -347,8 +257,11 @@
       return an_rpc if rpc_descs.key?(mth)
       GRPC.logger.warn("UNIMPLEMENTED: #{an_rpc}")
       noop = proc { |x| x }
+
+      # Create a new active call that knows that
+      # metadata hasn't been sent yet
       c = ActiveCall.new(an_rpc.call, noop, noop, an_rpc.deadline,
-                         metadata_received: true)
+                         metadata_received: true, started: false)
       c.send_status(GRPC::Core::StatusCodes::UNIMPLEMENTED, '')
       nil
     end
@@ -362,7 +275,7 @@
           break if (!an_rpc.nil?) && an_rpc.call.nil?
           active_call = new_active_server_call(an_rpc)
           unless active_call.nil?
-            @pool.schedule(active_call) do |ac|
+            @pool.post(active_call) do |ac|
               c, mth = ac
               begin
                 rpc_descs[mth].run_server_method(c, rpc_handlers[mth])
@@ -391,22 +304,24 @@
 
       # allow the metadata to be accessed from the call
       an_rpc.call.metadata = an_rpc.metadata  # attaches md to call for handlers
-      GRPC.logger.debug("call md is #{an_rpc.metadata}")
       connect_md = nil
       unless @connect_md_proc.nil?
         connect_md = @connect_md_proc.call(an_rpc.method, an_rpc.metadata)
       end
-      an_rpc.call.run_batch(SEND_INITIAL_METADATA => connect_md)
 
       return nil unless available?(an_rpc)
       return nil unless implemented?(an_rpc)
 
-      # Create the ActiveCall
+      # Create the ActiveCall. Indicate that metadata hasnt been sent yet.
       GRPC.logger.info("deadline is #{an_rpc.deadline}; (now=#{Time.now})")
       rpc_desc = rpc_descs[an_rpc.method.to_sym]
-      c = ActiveCall.new(an_rpc.call, rpc_desc.marshal_proc,
-                         rpc_desc.unmarshal_proc(:input), an_rpc.deadline,
-                         metadata_received: true)
+      c = ActiveCall.new(an_rpc.call,
+                         rpc_desc.marshal_proc,
+                         rpc_desc.unmarshal_proc(:input),
+                         an_rpc.deadline,
+                         metadata_received: true,
+                         started: false,
+                         metadata_to_send: connect_md)
       mth = an_rpc.method.to_sym
       [c, mth]
     end
diff --git a/src/ruby/lib/grpc/grpc.rb b/src/ruby/lib/grpc/grpc.rb
index b60a828..f46710d 100644
--- a/src/ruby/lib/grpc/grpc.rb
+++ b/src/ruby/lib/grpc/grpc.rb
@@ -28,7 +28,12 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 begin
-  require_relative "#{RUBY_VERSION.sub(/\.\d$/, '')}/grpc_c"
-rescue LoadError
-  require_relative 'grpc_c'
+  ruby_version_dirname = /(\d+\.\d+)/.match(RUBY_VERSION).to_s
+  distrib_lib_dir = File.expand_path(ruby_version_dirname,
+                                     File.dirname(__FILE__))
+  if File.directory?(distrib_lib_dir)
+    require_relative "#{distrib_lib_dir}/grpc_c"
+  else
+    require_relative 'grpc_c'
+  end
 end
diff --git a/src/ruby/pb/test/server.rb b/src/ruby/pb/test/server.rb
index 0808121..3f1e0a1 100755
--- a/src/ruby/pb/test/server.rb
+++ b/src/ruby/pb/test/server.rb
@@ -129,27 +129,36 @@
   [].pack('x' * l).force_encoding('ascii-8bit')
 end
 
-# A EnumeratorQueue wraps a Queue yielding the items added to it via each_item.
-class EnumeratorQueue
-  extend Forwardable
-  def_delegators :@q, :push
+# A FullDuplexEnumerator passes requests to a block and yields generated responses
+class FullDuplexEnumerator
+  include Grpc::Testing
+  include Grpc::Testing::PayloadType
 
-  def initialize(sentinel)
-    @q = Queue.new
-    @sentinel = sentinel
+  def initialize(requests)
+    @requests = requests
   end
-
   def each_item
     return enum_for(:each_item) unless block_given?
-    loop do
-      r = @q.pop
-      break if r.equal?(@sentinel)
-      fail r if r.is_a? Exception
-      yield r
+    GRPC.logger.info('interop-server: started receiving')
+    begin
+      cls = StreamingOutputCallResponse
+      @requests.each do |req|
+        req.response_parameters.each do |params|
+          resp_size = params.size
+          GRPC.logger.info("read a req, response size is #{resp_size}")
+          yield cls.new(payload: Payload.new(type: req.response_type,
+                                              body: nulls(resp_size)))
+        end
+      end
+      GRPC.logger.info('interop-server: finished receiving')
+    rescue StandardError => e
+      GRPC.logger.info('interop-server: failed')
+      GRPC.logger.warn(e)
+      fail e
     end
   end
 end
-
+    
 # A runnable implementation of the schema-specified testing service, with each
 # service method implemented as required by the interop testing spec.
 class TestTarget < Grpc::Testing::TestService::Service
@@ -182,31 +191,9 @@
 
   def full_duplex_call(reqs)
     # reqs is a lazy Enumerator of the requests sent by the client.
-    q = EnumeratorQueue.new(self)
-    cls = StreamingOutputCallResponse
-    Thread.new do
-      begin
-        GRPC.logger.info('interop-server: started receiving')
-        reqs.each do |req|
-          req.response_parameters.each do |params|
-            resp_size = params.size
-            GRPC.logger.info("read a req, response size is #{resp_size}")
-            resp = cls.new(payload: Payload.new(type: req.response_type,
-                                                body: nulls(resp_size)))
-            q.push(resp)
-          end
-        end
-        GRPC.logger.info('interop-server: finished receiving')
-        q.push(self)
-      rescue StandardError => e
-        GRPC.logger.info('interop-server: failed')
-        GRPC.logger.warn(e)
-        q.push(e)  # share the exception with the enumerator
-      end
-    end
-    q.each_item
+    FullDuplexEnumerator.new(reqs).each_item
   end
-
+        
   def half_duplex_call(reqs)
     # TODO: update with unique behaviour of the half_duplex_call if that's
     # ever required by any of the tests.
diff --git a/src/ruby/qps/client.rb b/src/ruby/qps/client.rb
index 7ed648a..8aed866 100644
--- a/src/ruby/qps/client.rb
+++ b/src/ruby/qps/client.rb
@@ -89,12 +89,14 @@
                    payload: gtp.new(type: gtpt::COMPRESSABLE,
                                     body: nulls(simple_params.req_size)))
 
+    @child_threads = []
+
     (0..config.client_channels-1).each do |chan|
       gtbss = Grpc::Testing::BenchmarkService::Stub
       st = config.server_targets
       stub = gtbss.new(st[chan % st.length], cred, **opts)
       (0..config.outstanding_rpcs_per_channel-1).each do |r|
-        Thread.new {
+        @child_threads << Thread.new {
           case config.load_params.load.to_s
           when 'closed_loop'
             waiter = nil
@@ -162,5 +164,8 @@
   end
   def shutdown
     @done = true
+    @child_threads.each do |thread|
+      thread.join
+    end
   end
 end
diff --git a/src/ruby/qps/qps-common.rb b/src/ruby/qps/qps-common.rb
index 4119d60..4714ccf 100644
--- a/src/ruby/qps/qps-common.rb
+++ b/src/ruby/qps/qps-common.rb
@@ -52,6 +52,7 @@
   files.map { |f| File.open(File.join(data_dir, f)).read }
 end
 
+
 # A EnumeratorQueue wraps a Queue yielding the items added to it via each_item.
 class EnumeratorQueue
   extend Forwardable
@@ -73,4 +74,19 @@
   end
 end
 
+# A PingPongEnumerator reads requests and responds one-by-one when enumerated
+# via #each_item
+class PingPongEnumerator
+  def initialize(reqs)
+    @reqs = reqs
+  end
 
+  def each_item
+    return enum_for(:each_item) unless block_given?
+    sr = Grpc::Testing::SimpleResponse
+    pl = Grpc::Testing::Payload
+    @reqs.each do |req|
+      yield sr.new(payload: pl.new(body: nulls(req.response_size)))
+    end
+  end
+end
diff --git a/src/ruby/qps/server.rb b/src/ruby/qps/server.rb
index cd98ee1..d0c2073 100644
--- a/src/ruby/qps/server.rb
+++ b/src/ruby/qps/server.rb
@@ -49,16 +49,7 @@
     sr.new(payload: pl.new(body: nulls(req.response_size)))
   end
   def streaming_call(reqs)
-    q = EnumeratorQueue.new(self)
-    Thread.new {
-      sr = Grpc::Testing::SimpleResponse
-      pl = Grpc::Testing::Payload
-      reqs.each do |req|
-        q.push(sr.new(payload: pl.new(body: nulls(req.response_size))))
-      end
-      q.push(self)
-    }
-    q.each_item
+    PingPongEnumerator.new(reqs).each_item
   end
 end
 
@@ -71,7 +62,8 @@
     else
       cred = :this_port_is_insecure
     end
-    @server = GRPC::RpcServer.new
+    # Make sure server can handle the large number of calls in benchmarks
+    @server = GRPC::RpcServer.new(pool_size: 100, max_waiting_requests: 100)
     @port = @server.add_http2_port("0.0.0.0:" + port.to_s, cred)
     @server.handle(BenchmarkServiceImpl.new)
     @start_time = Time.now
diff --git a/src/ruby/qps/worker.rb b/src/ruby/qps/worker.rb
index 12b8087..61a0b72 100755
--- a/src/ruby/qps/worker.rb
+++ b/src/ruby/qps/worker.rb
@@ -64,8 +64,8 @@
           q.push(gtss.new(stats: bms.mark(req.mark.reset), cores: cpu_cores))
         end
       end
-      q.push(self)
       bms.stop
+      q.push(self)
     }
     q.each_item
   end
@@ -83,8 +83,8 @@
                                                    client.mark(req.mark.reset)))
         end
       end
-      q.push(self)
       client.shutdown
+      q.push(self)
     }
     q.each_item
   end
@@ -118,6 +118,10 @@
       options['server_port'] = v
     end
   end.parse!
+
+  # Configure any errors with client or server child threads to surface
+  Thread.abort_on_exception = true
+  
   s = GRPC::RpcServer.new
   s.add_http2_port("0.0.0.0:" + options['driver_port'].to_s,
                    :this_port_is_insecure)
diff --git a/src/ruby/spec/generic/active_call_spec.rb b/src/ruby/spec/generic/active_call_spec.rb
index 018580e..48bc61e 100644
--- a/src/ruby/spec/generic/active_call_spec.rb
+++ b/src/ruby/spec/generic/active_call_spec.rb
@@ -60,8 +60,10 @@
     end
 
     describe '#multi_req_view' do
-      it 'exposes a fixed subset of the ActiveCall methods' do
-        want = %w(cancelled?, deadline, each_remote_read, metadata, shutdown)
+      it 'exposes a fixed subset of the ActiveCall.methods' do
+        want = %w(cancelled?, deadline, each_remote_read, metadata, \
+                  shutdown, peer, peer_cert, send_initial_metadata, \
+                  initial_metadata_sent)
         v = @client_call.multi_req_view
         want.each do |w|
           expect(v.methods.include?(w))
@@ -70,8 +72,10 @@
     end
 
     describe '#single_req_view' do
-      it 'exposes a fixed subset of the ActiveCall methods' do
-        want = %w(cancelled?, deadline, metadata, shutdown)
+      it 'exposes a fixed subset of the ActiveCall.methods' do
+        want = %w(cancelled?, deadline, metadata, shutdown, \
+                  send_initial_metadata, metadata_to_send, \
+                  merge_metadata_to_send, initial_metadata_sent)
         v = @client_call.single_req_view
         want.each do |w|
           expect(v.methods.include?(w))
@@ -149,6 +153,146 @@
     end
   end
 
+  describe 'sending initial metadata', send_initial_metadata: true do
+    it 'sends metadata before sending a message if it hasnt been sent yet' do
+      call = make_test_call
+      @client_call = ActiveCall.new(
+        call,
+        @pass_through,
+        @pass_through,
+        deadline,
+        started: false)
+
+      metadata = { key: 'dummy_val', other: 'other_val' }
+      expect(@client_call.metadata_sent).to eq(false)
+      @client_call.merge_metadata_to_send(metadata)
+
+      message = 'dummy message'
+
+      expect(call).to(
+        receive(:run_batch)
+          .with(
+            hash_including(
+              CallOps::SEND_INITIAL_METADATA => metadata)).once)
+
+      expect(call).to(
+        receive(:run_batch).with(hash_including(
+                                   CallOps::SEND_MESSAGE => message)).once)
+      @client_call.remote_send(message)
+
+      expect(@client_call.metadata_sent).to eq(true)
+    end
+
+    it 'doesnt send metadata if it thinks its already been sent' do
+      call = make_test_call
+
+      @client_call = ActiveCall.new(call,
+                                    @pass_through,
+                                    @pass_through,
+                                    deadline)
+
+      expect(@client_call.metadata_sent).to eql(true)
+      expect(call).to(
+        receive(:run_batch).with(hash_including(
+                                   CallOps::SEND_INITIAL_METADATA)).never)
+
+      @client_call.remote_send('test message')
+    end
+
+    it 'sends metadata if it is explicitly sent and ok to do so' do
+      call = make_test_call
+
+      @client_call = ActiveCall.new(call,
+                                    @pass_through,
+                                    @pass_through,
+                                    deadline,
+                                    started: false)
+
+      expect(@client_call.metadata_sent).to eql(false)
+
+      metadata = { test_key: 'val' }
+      @client_call.merge_metadata_to_send(metadata)
+      expect(@client_call.metadata_to_send).to eq(metadata)
+
+      expect(call).to(
+        receive(:run_batch).with(hash_including(
+                                   CallOps::SEND_INITIAL_METADATA =>
+                                     metadata)).once)
+      @client_call.send_initial_metadata
+    end
+
+    it 'explicit sending does nothing if metadata has already been sent' do
+      call = make_test_call
+
+      @client_call = ActiveCall.new(call,
+                                    @pass_through,
+                                    @pass_through,
+                                    deadline)
+
+      expect(@client_call.metadata_sent).to eql(true)
+
+      blk = proc do
+        @client_call.send_initial_metadata
+      end
+
+      expect { blk.call }.to_not raise_error
+    end
+  end
+
+  describe '#merge_metadata_to_send', merge_metadata_to_send: true do
+    it 'adds to existing metadata when there is existing metadata to send' do
+      call = make_test_call
+      starting_metadata = {
+        k1: 'key1_val',
+        k2: 'key2_val',
+        k3: 'key3_val'
+      }
+
+      @client_call = ActiveCall.new(
+        call,
+        @pass_through, @pass_through,
+        deadline,
+        started: false,
+        metadata_to_send: starting_metadata)
+
+      expect(@client_call.metadata_to_send).to eq(starting_metadata)
+
+      @client_call.merge_metadata_to_send(
+        k3: 'key3_new_val',
+        k4: 'key4_val')
+
+      expected_md_to_send = {
+        k1: 'key1_val',
+        k2: 'key2_val',
+        k3: 'key3_new_val',
+        k4: 'key4_val' }
+
+      expect(@client_call.metadata_to_send).to eq(expected_md_to_send)
+
+      @client_call.merge_metadata_to_send(k5: 'key5_val')
+      expected_md_to_send.merge!(k5: 'key5_val')
+      expect(@client_call.metadata_to_send).to eq(expected_md_to_send)
+    end
+
+    it 'fails when initial metadata has already been sent' do
+      call = make_test_call
+      @client_call = ActiveCall.new(
+        call,
+        @pass_through,
+        @pass_through,
+        deadline,
+        started: true)
+
+      expect(@client_call.metadata_sent).to eq(true)
+
+      blk = proc do
+        @client_call.merge_metadata_to_send(k1: 'key1_val')
+      end
+
+      expect { blk.call }.to raise_error
+    end
+  end
+
   describe '#client_invoke' do
     it 'sends metadata to the server when present' do
       call = make_test_call
@@ -163,7 +307,26 @@
     end
   end
 
-  describe '#remote_read' do
+  describe '#send_status', send_status: true do
+    it 'works when no metadata or messages have been sent yet' do
+      call = make_test_call
+      ActiveCall.client_invoke(call)
+
+      recvd_rpc = @server.request_call
+      server_call = ActiveCall.new(
+        recvd_rpc.call,
+        @pass_through,
+        @pass_through,
+        deadline,
+        started: false)
+
+      expect(server_call.metadata_sent).to eq(false)
+      blk = proc { server_call.send_status(OK) }
+      expect { blk.call }.to_not raise_error
+    end
+  end
+
+  describe '#remote_read', remote_read: true do
     it 'reads the response sent by a server' do
       call = make_test_call
       ActiveCall.client_invoke(call)
@@ -205,6 +368,31 @@
       expect(client_call.metadata).to eq(expected)
     end
 
+    it 'get a status from server when nothing else sent from server' do
+      client_call = make_test_call
+      ActiveCall.client_invoke(client_call)
+
+      recvd_rpc = @server.request_call
+      recvd_call = recvd_rpc.call
+
+      server_call = ActiveCall.new(
+        recvd_call,
+        @pass_through,
+        @pass_through,
+        deadline,
+        started: false)
+
+      server_call.send_status(OK, 'OK')
+
+      # Check that we can receive initial metadata and a status
+      client_call.run_batch(
+        CallOps::RECV_INITIAL_METADATA => nil)
+      batch_result = client_call.run_batch(
+        CallOps::RECV_STATUS_ON_CLIENT => nil)
+
+      expect(batch_result.status.code).to eq(OK)
+    end
+
     it 'get a nil msg before a status when an OK status is sent' do
       call = make_test_call
       ActiveCall.client_invoke(call)
@@ -329,6 +517,86 @@
     end
   end
 
+  # Test sending of the initial metadata in #run_server_bidi
+  # from the server handler both implicitly and explicitly.
+  describe '#run_server_bidi metadata sending tests', run_server_bidi: true do
+    before(:each) do
+      @requests = ['first message', 'second message']
+      @server_to_client_metadata = { 'test_key' => 'test_val' }
+      @server_status = OK
+
+      @client_call = make_test_call
+      @client_call.run_batch(CallOps::SEND_INITIAL_METADATA => {})
+
+      recvd_rpc = @server.request_call
+      recvd_call = recvd_rpc.call
+      @server_call = ActiveCall.new(
+        recvd_call,
+        @pass_through,
+        @pass_through,
+        deadline,
+        metadata_received: true,
+        started: false,
+        metadata_to_send: @server_to_client_metadata)
+    end
+
+    after(:each) do
+      # Send the requests and send a close so the server can send a status
+      @requests.each do |message|
+        @client_call.run_batch(CallOps::SEND_MESSAGE => message)
+      end
+      @client_call.run_batch(CallOps::SEND_CLOSE_FROM_CLIENT => nil)
+
+      @server_thread.join
+
+      # Expect that initial metadata was sent,
+      # the requests were echoed, and a status was sent
+      batch_result = @client_call.run_batch(
+        CallOps::RECV_INITIAL_METADATA => nil)
+      expect(batch_result.metadata).to eq(@server_to_client_metadata)
+
+      @requests.each do |message|
+        batch_result = @client_call.run_batch(
+          CallOps::RECV_MESSAGE => nil)
+        expect(batch_result.message).to eq(message)
+      end
+
+      batch_result = @client_call.run_batch(
+        CallOps::RECV_STATUS_ON_CLIENT => nil)
+      expect(batch_result.status.code).to eq(@server_status)
+    end
+
+    it 'sends the initial metadata implicitly if not already sent' do
+      # Server handler that doesn't have access to a "call"
+      # It echoes the requests
+      fake_gen_each_reply_with_no_call_param = proc do |msgs|
+        msgs
+      end
+
+      @server_thread = Thread.new do
+        @server_call.run_server_bidi(
+          fake_gen_each_reply_with_no_call_param)
+        @server_call.send_status(@server_status)
+      end
+    end
+
+    it 'sends the metadata when sent explicitly and not already sent' do
+      # Fake server handler that has access to a "call" object and
+      # uses it to explicitly update and send the initial metadata
+      fake_gen_each_reply_with_call_param = proc do |msgs, call_param|
+        call_param.merge_metadata_to_send(@server_to_client_metadata)
+        call_param.send_initial_metadata
+        msgs
+      end
+
+      @server_thread = Thread.new do
+        @server_call.run_server_bidi(
+          fake_gen_each_reply_with_call_param)
+        @server_call.send_status(@server_status)
+      end
+    end
+  end
+
   def expect_server_to_receive(sent_text, **kw)
     c = expect_server_to_be_invoked(**kw)
     expect(c.remote_read).to eq(sent_text)
diff --git a/src/ruby/spec/generic/rpc_desc_spec.rb b/src/ruby/spec/generic/rpc_desc_spec.rb
index d2080b7..1a89500 100644
--- a/src/ruby/spec/generic/rpc_desc_spec.rb
+++ b/src/ruby/spec/generic/rpc_desc_spec.rb
@@ -196,6 +196,9 @@
     def fake_svstream(_arg1, _arg2)
     end
 
+    def fake_three_args(_arg1, _arg2, _arg3)
+    end
+
     it 'raises when a request_response does not have 2 args' do
       [:fake_clstream, :no_arg].each do |mth|
         blk = proc do
@@ -244,8 +247,8 @@
       expect(&blk).to_not raise_error
     end
 
-    it 'raises when a bidi streamer does not have 1 arg' do
-      [:fake_svstream, :no_arg].each do |mth|
+    it 'raises when a bidi streamer does not have 1 or 2 args' do
+      [:fake_three_args, :no_arg].each do |mth|
         blk = proc do
           @bidi_streamer.assert_arity_matches(method(mth))
         end
@@ -259,6 +262,13 @@
       end
       expect(&blk).to_not raise_error
     end
+
+    it 'passes when a bidi streamer has 2 args' do
+      blk = proc do
+        @bidi_streamer.assert_arity_matches(method(:fake_svstream))
+      end
+      expect(&blk).to_not raise_error
+    end
   end
 
   describe '#request_response?' do
diff --git a/src/ruby/spec/generic/rpc_server_pool_spec.rb b/src/ruby/spec/generic/rpc_server_pool_spec.rb
deleted file mode 100644
index b67008d..0000000
--- a/src/ruby/spec/generic/rpc_server_pool_spec.rb
+++ /dev/null
@@ -1,138 +0,0 @@
-# Copyright 2015, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-require 'grpc'
-
-describe GRPC::Pool do
-  Pool = GRPC::Pool
-
-  describe '#new' do
-    it 'raises if a non-positive size is used' do
-      expect { Pool.new(0) }.to raise_error
-      expect { Pool.new(-1) }.to raise_error
-      expect { Pool.new(Object.new) }.to raise_error
-    end
-
-    it 'is constructed OK with a positive size' do
-      expect { Pool.new(1) }.not_to raise_error
-    end
-  end
-
-  describe '#jobs_waiting' do
-    it 'at start, it is zero' do
-      p = Pool.new(1)
-      expect(p.jobs_waiting).to be(0)
-    end
-
-    it 'it increases, with each scheduled job if the pool is not running' do
-      p = Pool.new(1)
-      job = proc {}
-      expect(p.jobs_waiting).to be(0)
-      5.times do |i|
-        p.schedule(&job)
-        expect(p.jobs_waiting).to be(i + 1)
-      end
-    end
-
-    it 'it decreases as jobs are run' do
-      p = Pool.new(1)
-      job = proc {}
-      expect(p.jobs_waiting).to be(0)
-      3.times do
-        p.schedule(&job)
-      end
-      p.start
-      sleep 2
-      expect(p.jobs_waiting).to be(0)
-    end
-  end
-
-  describe '#schedule' do
-    it 'return if the pool is already stopped' do
-      p = Pool.new(1)
-      p.stop
-      job = proc {}
-      expect { p.schedule(&job) }.to_not raise_error
-    end
-
-    it 'adds jobs that get run by the pool' do
-      p = Pool.new(1)
-      p.start
-      o, q = Object.new, Queue.new
-      job = proc { q.push(o) }
-      p.schedule(&job)
-      expect(q.pop).to be(o)
-      p.stop
-    end
-  end
-
-  describe '#stop' do
-    it 'works when there are no scheduled tasks' do
-      p = Pool.new(1)
-      expect { p.stop }.not_to raise_error
-    end
-
-    it 'stops jobs when there are long running jobs' do
-      p = Pool.new(1)
-      p.start
-      o, q = Object.new, Queue.new
-      job = proc do
-        sleep(5)  # long running
-        q.push(o)
-      end
-      p.schedule(&job)
-      sleep(1)  # should ensure the long job gets scheduled
-      expect { p.stop }.not_to raise_error
-    end
-  end
-
-  describe '#start' do
-    it 'runs pre-scheduled jobs' do
-      p = Pool.new(2)
-      o, q = Object.new, Queue.new
-      n = 5  # arbitrary
-      n.times { p.schedule(o, &q.method(:push)) }
-      p.start
-      n.times { expect(q.pop).to be(o) }
-      p.stop
-    end
-
-    it 'runs jobs as they are scheduled ' do
-      p = Pool.new(2)
-      o, q = Object.new, Queue.new
-      p.start
-      n = 5  # arbitrary
-      n.times do
-        p.schedule(o, &q.method(:push))
-        expect(q.pop).to be(o)
-      end
-      p.stop
-    end
-  end
-end
diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb
index 31157cf..d362e48 100644
--- a/src/ruby/spec/generic/rpc_server_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_spec.rb
@@ -395,9 +395,9 @@
       it 'should return RESOURCE_EXHAUSTED on too many jobs', server: true do
         opts = {
           server_args: { a_channel_arg: 'an_arg' },
-          pool_size: 1,
+          pool_size: 2,
           poll_period: 1,
-          max_waiting_requests: 0
+          max_waiting_requests: 1
         }
         alt_srv = RpcServer.new(**opts)
         alt_srv.handle(SlowService)
@@ -406,24 +406,23 @@
         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
+        n = 20 # arbitrary, use as many to ensure the server pool is exceeded
         threads = []
-        one_failed_as_unavailable = false
+        bad_status_code = nil
         n.times do
           threads << Thread.new do
             stub = SlowStub.new(alt_host, :this_channel_is_insecure)
             begin
               stub.an_rpc(req)
             rescue GRPC::BadStatus => e
-              one_failed_as_unavailable =
-                e.code == StatusCodes::RESOURCE_EXHAUSTED
+              bad_status_code = e.code
             end
           end
         end
         threads.each(&:join)
         alt_srv.stop
         t.join
-        expect(one_failed_as_unavailable).to be(true)
+        expect(bad_status_code).to be(StatusCodes::RESOURCE_EXHAUSTED)
       end
     end
 
diff --git a/summerofcode/2016/siddharth_shukla.md b/summerofcode/2016/siddharth_shukla.md
new file mode 100644
index 0000000..d753d8b
--- /dev/null
+++ b/summerofcode/2016/siddharth_shukla.md
@@ -0,0 +1,65 @@
+Project Overview
+================
+The project, titled 'GRPC Python compatibility support', involved 
+collaborating with the GRPC team to improve the library compatibility 
+for the GRPC Python library.
+
+Python is, originally, a specification for a programming language. This
+specification has been implemented differently in different
+implementations of the [language specification](https://docs.python.org/3/reference/). 
+
+A small, and by no means exhaustive, list of some major python implementations
+is:
+
+- [CPython](https://www.python.org/): The reference implementation
+- [Jython](http://www.jython.org/): Python implemented in Java
+- [Python for .NET](http://pythonnet.sourceforge.net/): CPython implementation that enables .NET library usage
+- [IronPython](http://ironpython.net/): Python implemented in .NET
+- [PyPy](http://pypy.org/): Python implemented completely in Python
+- [Stackless](https://bitbucket.org/stackless-dev/stackless/wiki/Home): Replaces the dependency for the C call stack with it's own stack
+
+The development in this project revolved around
+introducing changes to the codebase that enable support for latest 
+stable as well as development releases of the reference implementation 
+(CPython) of the Python programming language namely `Python 3.4`,
+`Python 3.5`,and `Python 3.6` as well as the stable releases of the 
+PyPy implementation. Special changes were required to enable PyPy 
+support because PyPy has a non-deterministic garbage collector that does
+not rely on reference counting unlike the CPython garbage collector.
+
+The changes to the codebase involved changes to the library code as well
+as changes to the tests and scripts in the test infrastructure which
+resulted in both the library as well as the testing infrastructure being
+Python 3.x and PyPy compatible.
+
+The list of merged commits, as of 22.08.2016 23:59 CEST,  is summarized 
+here for the perusal of those interested:
+
+- [Enable py35 and py36 testing](https://github.com/grpc/grpc/commit/c478214e475e103c5cdf477f0adc18bba2c03903)
+- [Make testing toolchain python 3.x compliant](https://github.com/grpc/grpc/commit/0589e533cd65a2ca9e0e610cc1b284d016986572)
+- [Add .idea folder to .gitignore](https://github.com/grpc/grpc/commit/365ef40947e22b5438a63f123679ae9a5474c47c)
+- [Fix the ThreadPoolExecutor: max_workers can't be 0](https://github.com/grpc/grpc/commit/de84d566b8fad6808e5263a25a17fa231cb5713c)
+- [Add PyPy to testing toolchain](https://github.com/grpc/grpc/commit/2135a1b557f8b992186d5317cb767ac4dbcdfe5c)
+- [Switch init/shutdown: lib-wide -> per-object](https://github.com/grpc/grpc/commit/9eedb4ffd74aed8d246a07f8007960b2bc167f55)
+- [Skip test run if running with pypy](https://github.com/grpc/grpc/commit/f0f58e68738abbc317f7f449c5104f7fbbff26bd)
+
+The list of unmerged pull requests is as follows:
+
+- [Add PyPy 5.3.1 to dockerfile and template](https://github.com/grpc/grpc/pull/7763)
+- [remove skipIf from TypeSmokeTest (issue 7672)](https://github.com/grpc/grpc/pull/7831)
+  
+The list of tasks that have pending unsubmitted pull requests is as follows:
+
+- Modify run_tests.py to enable testing of new languages without
+  affecting old branches.
+
+
+Project Details
+===============
+- Title: GRPC Python compatibility support
+- Student: [Siddharth Shukla](https://github.com/thunderboltsid)
+- Mentors: [Nathaniel Manista](https://github.com/nathanielmanistaatgoogle), [Masood Malekghassemi](https://github.com/soltanmm)
+- Duration: May 23 - August 23
+- Hat tip: [Ken Payson](https://github.com/kpayson64), [Jan Tattermusch](https://github.com/jtattermusch), [Nicolas Noble](https://github.com/nicolasnoble)
+
+
diff --git a/templates/BUILD.template b/templates/BUILD.template
index 23a656c..af23fb2 100644
--- a/templates/BUILD.template
+++ b/templates/BUILD.template
@@ -38,6 +38,8 @@
   
   licenses(["notice"])  # 3-clause BSD
   
+  exports_files(["LICENSE"])
+  
   package(default_visibility = ["//visibility:public"])
   
   <%!
diff --git a/templates/CMakeLists.txt.template b/templates/CMakeLists.txt.template
index 4e42234..2b6c0b1 100644
--- a/templates/CMakeLists.txt.template
+++ b/templates/CMakeLists.txt.template
@@ -66,6 +66,12 @@
   set(PACKAGE_BUGREPORT "https://github.com/grpc/grpc/issues/")
   project(<%text>${PACKAGE_NAME}</%text> C CXX)
 
+  if (NOT MSVC)
+    set(gRPC_INSTALL ON CACHE BOOL "Generate installation target")
+  else()
+    set(gRPC_INSTALL OFF CACHE BOOL "Generate installation target")
+  endif()
+
   set(gRPC_ZLIB_PROVIDER "module" CACHE STRING "Provider of zlib library")
   set_property(CACHE gRPC_ZLIB_PROVIDER PROPERTY STRINGS "module" "package")
 
@@ -77,6 +83,10 @@
 
   set(gRPC_USE_PROTO_LITE OFF CACHE BOOL "Use the protobuf-lite library")
 
+  if (MSVC)
+    add_definitions( -D_WIN32_WINNT=0x600 )
+  endif()
+
   if (gRPC_USE_PROTO_LITE)
     set(_gRPC_PROTOBUF_LIBRARY_NAME "libprotobuf-lite")
     add_definitions("-DGRPC_USE_PROTO_LITE")
@@ -116,6 +126,7 @@
       set(PROTOBUF_ROOT_DIR <%text>${CMAKE_CURRENT_SOURCE_DIR}</%text>/third_party/protobuf)
     endif()
     if(EXISTS "<%text>${PROTOBUF_ROOT_DIR}</%text>/cmake/CMakeLists.txt")
+      set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Link static runtime libraries")
       add_subdirectory(<%text>${PROTOBUF_ROOT_DIR}</%text>/cmake third_party/protobuf)
       if(TARGET <%text>${_gRPC_PROTOBUF_LIBRARY_NAME}</%text>)
         set(_gRPC_PROTOBUF_LIBRARIES <%text>${_gRPC_PROTOBUF_LIBRARY_NAME}</%text>)
@@ -255,17 +266,21 @@
   </%def>
 
   <%def name="cc_install(tgt)">
-  install(TARGETS ${tgt.name} EXPORT gRPCTargets
-    RUNTIME DESTINATION <%text>${CMAKE_INSTALL_BINDIR}</%text>
-    LIBRARY DESTINATION <%text>${CMAKE_INSTALL_LIBDIR}</%text>
-    ARCHIVE DESTINATION <%text>${CMAKE_INSTALL_LIBDIR}</%text>
-  )
+  if (gRPC_INSTALL)
+    install(TARGETS ${tgt.name} EXPORT gRPCTargets
+      RUNTIME DESTINATION <%text>${CMAKE_INSTALL_BINDIR}</%text>
+      LIBRARY DESTINATION <%text>${CMAKE_INSTALL_LIBDIR}</%text>
+      ARCHIVE DESTINATION <%text>${CMAKE_INSTALL_LIBDIR}</%text>
+    )
+  endif()
   </%def>
 
-  install(EXPORT gRPCTargets
-    DESTINATION <%text>${CMAKE_INSTALL_CMAKEDIR}</%text>
-    NAMESPACE gRPC::
-  )
+  if (gRPC_INSTALL)
+    install(EXPORT gRPCTargets
+      DESTINATION <%text>${CMAKE_INSTALL_CMAKEDIR}</%text>
+      NAMESPACE gRPC::
+    )
+  endif()
 
   foreach(_config gRPCConfig gRPCConfigVersion)
     configure_file(tools/cmake/<%text>${_config}</%text>.cmake.in
diff --git a/templates/Makefile.template b/templates/Makefile.template
index 9afc656..e6a28d1 100644
--- a/templates/Makefile.template
+++ b/templates/Makefile.template
@@ -380,7 +380,7 @@
   OPENSSL_ALPN_CHECK_CMD = $(PKG_CONFIG) --atleast-version=1.0.2 openssl
   OPENSSL_NPN_CHECK_CMD = $(PKG_CONFIG) --atleast-version=1.0.1 openssl
   ZLIB_CHECK_CMD = $(PKG_CONFIG) --exists zlib
-  PROTOBUF_CHECK_CMD = $(PKG_CONFIG) --atleast-version=3.0.0-alpha-3 protobuf
+  PROTOBUF_CHECK_CMD = $(PKG_CONFIG) --atleast-version=3.0.0 protobuf
   else # HAS_PKG_CONFIG
 
   ifeq ($(SYSTEM),MINGW32)
@@ -694,13 +694,6 @@
   NO_DEPS = true
   endif
 
-  INSTALL_OK = false
-  ifeq ($(HAS_VALID_PROTOC),true)
-  ifeq ($(HAS_SYSTEM_PROTOBUF_VERIFY),true)
-  INSTALL_OK = true
-  endif
-  endif
-
   .SECONDARY = %.pb.h %.pb.cc
 
   ifeq ($(DEP_MISSING),)
@@ -1193,7 +1186,7 @@
   	$(Q) mkdir -p `dirname $@`
   	$(Q) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -MMD -MF $(addsuffix .dep, $(basename $@)) -c -o $@ $<
 
-  install: install_c install_cxx install-plugins install-certs verify-install
+  install: install_c install_cxx install-plugins install-certs
 
   install_c: install-headers_c install-static_c install-shared_c
 
@@ -1311,28 +1304,6 @@
   	$(Q) $(INSTALL) -d $(prefix)/share/grpc
   	$(Q) $(INSTALL) etc/roots.pem $(prefix)/share/grpc/roots.pem
 
-  verify-install:
-  ifeq ($(INSTALL_OK),true)
-  	@echo "Your system looks ready to go."
-  	@echo
-  else
-  	@echo "Warning: it looks like protoc 3.0.0+ isn't installed on your system,"
-  	@echo "which means that you won't be able to compile .proto files for use"
-  	@echo "with gRPC."
-  	@echo
-  	@echo "If you are just using pre-compiled protocol buffers, or you otherwise"
-  	@echo "have no need to compile .proto files, you can ignore this."
-  	@echo
-  	@echo "If you do need protobuf for some reason, you can download and install"
-  	@echo "it from:"
-  	@echo
-  	@echo "   https://github.com/google/protobuf/releases"
-  	@echo
-  	@echo "Once you've done so, you can re-run this check by doing:"
-  	@echo
-  	@echo "   make verify-install"
-  endif
-
   clean:
   	$(E) "[CLEAN]   Cleaning build directories."
   	$(Q) $(RM) -rf $(OBJDIR) $(LIBDIR) $(BINDIR) $(GENDIR) cache.mk
diff --git a/templates/composer.json.template b/templates/composer.json.template
index 48d3b88..accfb38 100644
--- a/templates/composer.json.template
+++ b/templates/composer.json.template
@@ -9,7 +9,7 @@
     "license": "BSD-3-Clause",
     "require": {
       "php": ">=5.5.0",
-      "stanley-cheung/protobuf-php": "v0.6"
+      "google/protobuf": "v3.1.0-alpha-1"
     },
     "require-dev": {
       "google/auth": "v0.9"
diff --git a/templates/gRPC-Core.podspec.template b/templates/gRPC-Core.podspec.template
index 12d06a7..1d5a473 100644
--- a/templates/gRPC-Core.podspec.template
+++ b/templates/gRPC-Core.podspec.template
@@ -62,7 +62,7 @@
   %>
   Pod::Spec.new do |s|
     s.name     = 'gRPC-Core'
-    version = '1.0.0-pre1'
+    version = '1.0.0'
     s.version  = version
     s.summary  = 'Core cross-platform gRPC library, written in C'
     s.homepage = 'http://www.grpc.io'
@@ -71,7 +71,7 @@
 
     s.source = {
       :git => 'https://github.com/grpc/grpc.git',
-      :tag => "objective-c-v#{version}",
+      :tag => "v#{version}",
       # TODO(jcanizales): Depend explicitly on the nanopb pod, and disable submodules.
       :submodules => true,
     }
@@ -149,7 +149,7 @@
       ss.header_mappings_dir = '.'
       ss.libraries = 'z'
       ss.dependency "#{s.name}/Interface", version
-      ss.dependency 'BoringSSL', '~> 5.0'
+      ss.dependency 'BoringSSL', '~> 6.0'
 
       # To save you from scrolling, this is the last part of the podspec.
       ss.source_files = ${ruby_multiline_list(grpc_private_files(libs), 22)}
@@ -162,12 +162,16 @@
       ss.source_files = 'include/grpc/grpc_cronet.h'
     end
 
-    s.subspec 'Cronet-Tests' do |ss|
+    s.subspec 'Cronet-Implementation' do |ss|
+      ss.header_mappings_dir = '.'
+      ss.source_files = 'src/core/ext/transport/cronet/client/secure/cronet_channel_create.c',
+                        'src/core/ext/transport/cronet/transport/cronet_transport.c'
+    end
+
+    s.subspec 'Tests' do |ss|
       ss.header_mappings_dir = '.'
 
-      ss.source_files = 'src/core/ext/transport/cronet/client/secure/cronet_channel_create.c',
-                        'src/core/ext/transport/cronet/transport/cronet_transport.c',
-                        'test/core/end2end/cq_verifier.{c,h}',
+      ss.source_files = 'test/core/end2end/cq_verifier.{c,h}',
                         'test/core/end2end/end2end_tests.{c,h}',
                         'test/core/end2end/tests/*.{c,h}',
                         'test/core/end2end/data/*.{c,h}',
diff --git a/templates/grpc.gemspec.template b/templates/grpc.gemspec.template
index ce775ff..d80248c 100644
--- a/templates/grpc.gemspec.template
+++ b/templates/grpc.gemspec.template
@@ -29,8 +29,9 @@
     s.require_paths = %w( src/ruby/bin src/ruby/lib src/ruby/pb )
     s.platform      = Gem::Platform::RUBY
 
-    s.add_dependency 'google-protobuf', '~> 3.0.0.alpha.5.0.3'
+    s.add_dependency 'google-protobuf', '~> 3.0.2'
     s.add_dependency 'googleauth',      '~> 0.5.1'
+    s.add_dependency 'concurrent-ruby'
 
     s.add_development_dependency 'bundler',            '~> 1.9'
     s.add_development_dependency 'facter',             '~> 2.4'
diff --git a/templates/package.json.template b/templates/package.json.template
index f68f64d..e9596d4 100644
--- a/templates/package.json.template
+++ b/templates/package.json.template
@@ -37,7 +37,7 @@
     "devDependencies": {
       "async": "^1.5.0",
       "google-auth-library": "^0.9.2",
-      "google-protobuf": "^3.0.0-alpha.5",
+      "google-protobuf": "^3.0.0",
       "istanbul": "^0.3.21",
       "jsdoc": "^3.3.2",
       "jshint": "^2.5.0",
diff --git a/templates/package.xml.template b/templates/package.xml.template
index 87b1038..32ed3b6 100644
--- a/templates/package.xml.template
+++ b/templates/package.xml.template
@@ -12,7 +12,7 @@
     <email>grpc-packages@google.com</email>
     <active>yes</active>
    </lead>
-   <date>2016-07-28</date>
+   <date>2016-08-22</date>
    <time>16:06:07</time>
    <version>
     <release>${settings.php_version.php()}</release>
@@ -24,7 +24,7 @@
    </stability>
    <license>BSD</license>
    <notes>
-  - PHP7 Support continued, reduce code duplication #7543
+  - Reject metadata keys which are not legal #7881
    </notes>
    <contents>
     <dir baseinstalldir="/" name="/">
@@ -249,5 +249,50 @@
   - PHP7 Support continued, reduce code duplication #7543
      </notes>
     </release>
+    <release>
+     <version>
+      <release>1.0.0RC4</release>
+      <api>1.0.0RC4</api>
+     </version>
+     <stability>
+      <release>stable</release>
+      <api>stable</api>
+     </stability>
+     <date>2016-08-09</date>
+     <license>BSD</license>
+     <notes>
+  - Fixed Ubuntu compile error #7571, #7642
+     </notes>
+    </release>
+    <release>
+     <version>
+      <release>1.0.0</release>
+      <api>1.0.0</api>
+     </version>
+     <stability>
+      <release>stable</release>
+      <api>stable</api>
+     </stability>
+     <date>2016-08-18</date>
+     <license>BSD</license>
+     <notes>
+  - gRPC 1.0.0 release
+     </notes>
+    </release>
+    <release>
+     <version>
+      <release>${settings.php_version.php()}</release>
+      <api>${settings.php_version.php()}</api>
+     </version>
+     <stability>
+      <release>stable</release>
+      <api>stable</api>
+     </stability>
+     <date>2016-08-22</date>
+     <license>BSD</license>
+     <notes>
+  - Reject metadata keys which are not legal #7881
+     </notes>
+    </release>
    </changelog>
   </package>
diff --git a/templates/src/core/lib/surface/version.c.template b/templates/src/core/lib/surface/version.c.template
index 5f0273e..12c490e 100644
--- a/templates/src/core/lib/surface/version.c.template
+++ b/templates/src/core/lib/surface/version.c.template
@@ -32,10 +32,12 @@
    * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *
    */
-  
+
   /* This file is autogenerated from:
      templates/src/core/surface/version.c.template */
-  
+
   #include <grpc/grpc.h>
-  
+
   const char *grpc_version_string(void) { return "${settings.core_version}"; }
+
+  const char *grpc_g_stands_for(void) { return "${settings.g_stands_for}"; }
diff --git a/templates/src/csharp/Grpc.Auth/project.json.template b/templates/src/csharp/Grpc.Auth/project.json.template
index d91bd8c..939a0c8 100644
--- a/templates/src/csharp/Grpc.Auth/project.json.template
+++ b/templates/src/csharp/Grpc.Auth/project.json.template
@@ -25,18 +25,13 @@
     },
     "dependencies": {
       "Grpc.Core": "${settings.csharp_version}",
-      "Google.Apis.Auth": "1.11.1"
+      "Google.Apis.Auth": "1.16.0"
     },
     "frameworks": {
       "net45": { },
       "netstandard1.5": {
-        "imports": [
-          "net45"
-        ],
         "dependencies": {
-          "Microsoft.NETCore.Portable.Compatibility": "1.0.1-rc2-24027",
-          "NETStandard.Library": "1.5.0-rc2-24027",
-          "System.Threading.Tasks": "4.0.11-rc2-24027"
+          "NETStandard.Library": "1.6.0"
         }
       }
     }
diff --git a/templates/src/csharp/Grpc.Core.Tests/project.json.template b/templates/src/csharp/Grpc.Core.Tests/project.json.template
index d1ab931..8a3e075 100644
--- a/templates/src/csharp/Grpc.Core.Tests/project.json.template
+++ b/templates/src/csharp/Grpc.Core.Tests/project.json.template
@@ -15,12 +15,15 @@
     },
     "frameworks": {
       "net45": { },
-      "netstandard1.5": {
+      "netcoreapp1.0": {
         "imports": [
           "portable-net45"
         ],
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027"
+          "Microsoft.NETCore.App": {
+            "type": "platform",
+            "version": "1.0.0"
+          }
         }
       }
     },
diff --git a/templates/src/csharp/Grpc.Core/project.json.template b/templates/src/csharp/Grpc.Core/project.json.template
index 6355db5..fcbef53 100644
--- a/templates/src/csharp/Grpc.Core/project.json.template
+++ b/templates/src/csharp/Grpc.Core/project.json.template
@@ -33,17 +33,15 @@
       "xmlDoc": true
     },
     "dependencies": {
-      "Ix-Async": "1.2.5"
+      "System.Interactive.Async": "3.0.0"
     },
     "frameworks": {
       "net45": { },
       "netstandard1.5": {
-        "imports": [
-          "portable-net45"
-        ],
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027",
-          "System.Threading.Thread": "4.0.0-rc2-24027"
+          "NETStandard.Library": "1.6.0",
+          "System.Runtime.Loader": "4.0.0",
+          "System.Threading.Thread": "4.0.0"
         }
       }
     }
diff --git a/templates/src/csharp/Grpc.Examples.MathClient/project.json.template b/templates/src/csharp/Grpc.Examples.MathClient/project.json.template
index fba401c..ae4ea2a 100644
--- a/templates/src/csharp/Grpc.Examples.MathClient/project.json.template
+++ b/templates/src/csharp/Grpc.Examples.MathClient/project.json.template
@@ -9,12 +9,12 @@
     },
     "frameworks": {
       "net45": { },
-      "netstandard1.5": {
-        "imports": [
-          "portable-net45"
-        ],
+      "netcoreapp1.0": {
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027"
+          "Microsoft.NETCore.App": {
+            "type": "platform",
+            "version": "1.0.0"
+          }
         }
       }
     }
diff --git a/templates/src/csharp/Grpc.Examples.MathServer/project.json.template b/templates/src/csharp/Grpc.Examples.MathServer/project.json.template
index fba401c..ae4ea2a 100644
--- a/templates/src/csharp/Grpc.Examples.MathServer/project.json.template
+++ b/templates/src/csharp/Grpc.Examples.MathServer/project.json.template
@@ -9,12 +9,12 @@
     },
     "frameworks": {
       "net45": { },
-      "netstandard1.5": {
-        "imports": [
-          "portable-net45"
-        ],
+      "netcoreapp1.0": {
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027"
+          "Microsoft.NETCore.App": {
+            "type": "platform",
+            "version": "1.0.0"
+          }
         }
       }
     }
diff --git a/templates/src/csharp/Grpc.Examples.Tests/project.json.template b/templates/src/csharp/Grpc.Examples.Tests/project.json.template
index 21765f0..0a9eb7c 100644
--- a/templates/src/csharp/Grpc.Examples.Tests/project.json.template
+++ b/templates/src/csharp/Grpc.Examples.Tests/project.json.template
@@ -11,12 +11,15 @@
     },
     "frameworks": {
       "net45": { },
-      "netstandard1.5": {
+      "netcoreapp1.0": {
         "imports": [
           "portable-net45"
         ],
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027"
+          "Microsoft.NETCore.App": {
+            "type": "platform",
+            "version": "1.0.0"
+          }
         }
       }
     }
diff --git a/templates/src/csharp/Grpc.Examples/project.json.template b/templates/src/csharp/Grpc.Examples/project.json.template
index 715fc08..b8a8314 100644
--- a/templates/src/csharp/Grpc.Examples/project.json.template
+++ b/templates/src/csharp/Grpc.Examples/project.json.template
@@ -6,7 +6,7 @@
       "Grpc.Core": {
         "target": "project"
       },
-      "Google.Protobuf": "3.0.0-beta3"
+      "Google.Protobuf": "3.0.0"
     },
     "frameworks": {
       "net45": {
@@ -15,12 +15,12 @@
           "System.IO": ""
         }
       },
-      "netstandard1.5": {
-        "imports": [
-          "portable-net45"
-        ],
+      "netcoreapp1.0": {
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027"
+          "Microsoft.NETCore.App": {
+            "type": "platform",
+            "version": "1.0.0"
+          }
         }
       }
     }
diff --git a/templates/src/csharp/Grpc.HealthCheck.Tests/project.json.template b/templates/src/csharp/Grpc.HealthCheck.Tests/project.json.template
index 79e6722..c63da96 100644
--- a/templates/src/csharp/Grpc.HealthCheck.Tests/project.json.template
+++ b/templates/src/csharp/Grpc.HealthCheck.Tests/project.json.template
@@ -11,12 +11,15 @@
     },
     "frameworks": {
       "net45": { },
-      "netstandard1.5": {
+      "netcoreapp1.0": {
         "imports": [
           "portable-net45"
         ],
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027"
+          "Microsoft.NETCore.App": {
+            "type": "platform",
+            "version": "1.0.0"
+          }
         }
       }
     }
diff --git a/templates/src/csharp/Grpc.HealthCheck/project.json.template b/templates/src/csharp/Grpc.HealthCheck/project.json.template
index 264ed29..46307dd 100644
--- a/templates/src/csharp/Grpc.HealthCheck/project.json.template
+++ b/templates/src/csharp/Grpc.HealthCheck/project.json.template
@@ -25,7 +25,7 @@
     },
     "dependencies": {
       "Grpc.Core": "${settings.csharp_version}",
-      "Google.Protobuf": "3.0.0-beta3"
+      "Google.Protobuf": "3.0.0"
     },
     "frameworks": {
       "net45": {
@@ -35,11 +35,8 @@
         }
       },
       "netstandard1.5": {
-        "imports": [
-          "portable-net45"
-        ],
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027"
+          "NETStandard.Library": "1.6.0"
         }
       }
     }
diff --git a/templates/src/csharp/Grpc.IntegrationTesting.Client/project.json.template b/templates/src/csharp/Grpc.IntegrationTesting.Client/project.json.template
index 10ed549..83b8a9b 100644
--- a/templates/src/csharp/Grpc.IntegrationTesting.Client/project.json.template
+++ b/templates/src/csharp/Grpc.IntegrationTesting.Client/project.json.template
@@ -9,13 +9,15 @@
     },
     "frameworks": {
       "net45": { },
-      "netstandard1.5": {
+      "netcoreapp1.0": {
         "imports": [
-          "portable-net45",
-          "net45"
+          "portable-net45"
         ],
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027"
+          "Microsoft.NETCore.App": {
+            "type": "platform",
+            "version": "1.0.0"
+          }
         }
       }
     }
diff --git a/templates/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json.template b/templates/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json.template
index 10ed549..83b8a9b 100644
--- a/templates/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json.template
+++ b/templates/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json.template
@@ -9,13 +9,15 @@
     },
     "frameworks": {
       "net45": { },
-      "netstandard1.5": {
+      "netcoreapp1.0": {
         "imports": [
-          "portable-net45",
-          "net45"
+          "portable-net45"
         ],
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027"
+          "Microsoft.NETCore.App": {
+            "type": "platform",
+            "version": "1.0.0"
+          }
         }
       }
     }
diff --git a/templates/src/csharp/Grpc.IntegrationTesting.Server/project.json.template b/templates/src/csharp/Grpc.IntegrationTesting.Server/project.json.template
index 10ed549..83b8a9b 100644
--- a/templates/src/csharp/Grpc.IntegrationTesting.Server/project.json.template
+++ b/templates/src/csharp/Grpc.IntegrationTesting.Server/project.json.template
@@ -9,13 +9,15 @@
     },
     "frameworks": {
       "net45": { },
-      "netstandard1.5": {
+      "netcoreapp1.0": {
         "imports": [
-          "portable-net45",
-          "net45"
+          "portable-net45"
         ],
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027"
+          "Microsoft.NETCore.App": {
+            "type": "platform",
+            "version": "1.0.0"
+          }
         }
       }
     }
diff --git a/templates/src/csharp/Grpc.IntegrationTesting.StressClient/project.json.template b/templates/src/csharp/Grpc.IntegrationTesting.StressClient/project.json.template
index 10ed549..83b8a9b 100644
--- a/templates/src/csharp/Grpc.IntegrationTesting.StressClient/project.json.template
+++ b/templates/src/csharp/Grpc.IntegrationTesting.StressClient/project.json.template
@@ -9,13 +9,15 @@
     },
     "frameworks": {
       "net45": { },
-      "netstandard1.5": {
+      "netcoreapp1.0": {
         "imports": [
-          "portable-net45",
-          "net45"
+          "portable-net45"
         ],
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027"
+          "Microsoft.NETCore.App": {
+            "type": "platform",
+            "version": "1.0.0"
+          }
         }
       }
     }
diff --git a/templates/src/csharp/Grpc.IntegrationTesting/project.json.template b/templates/src/csharp/Grpc.IntegrationTesting/project.json.template
index 3181511..3ce94e5 100644
--- a/templates/src/csharp/Grpc.IntegrationTesting/project.json.template
+++ b/templates/src/csharp/Grpc.IntegrationTesting/project.json.template
@@ -9,29 +9,29 @@
       "Grpc.Core": {
         "target": "project"
       },
-      "Google.Protobuf": "3.0.0-beta3",
-      "CommandLineParser": "1.9.71",
+      "Google.Protobuf": "3.0.0",
+      "CommandLineParser.Unofficial": "2.0.275",
+      "Moq": "4.6.38-alpha",
       "NUnit": "3.2.0",
       "NUnitLite": "3.2.0-*"
     },
     "frameworks": {
       "net45": {
-        "dependencies": {
-          "Moq": "4.2.1510.2205"
-        },
         "frameworkAssemblies": {
           "System.Runtime": "",
           "System.IO": ""
         }
       },
-      "netstandard1.5": {
+      "netcoreapp1.0": {
         "imports": [
-          "portable-net45",
-          "net45"
+          "portable-net45"
         ],
         "dependencies": {
-          "NETStandard.Library": "1.5.0-rc2-24027",
-          "System.Linq.Expressions": "4.0.11-rc2-24027"
+          "Microsoft.NETCore.App": {
+            "type": "platform",
+            "version": "1.0.0"
+          },
+          "System.Linq.Expressions": "4.1.0"
         }
       }
     }
diff --git a/templates/src/csharp/build_options.include b/templates/src/csharp/build_options.include
index 169a45a..bda2d76 100644
--- a/templates/src/csharp/build_options.include
+++ b/templates/src/csharp/build_options.include
@@ -16,10 +16,12 @@
           "includeFiles": [ "../Grpc.Core/Version.cs" ]
         },
         "copyToOutput": {
-          % if includeData:
-          "include": "data/*",
-          % endif
           "mappings": {
+            % if includeData:
+            "data/ca.pem": "../Grpc.IntegrationTesting/data/ca.pem",
+            "data/server1.key": "../Grpc.IntegrationTesting/data/server1.key",
+            "data/server1.pem": "../Grpc.IntegrationTesting/data/server1.pem",
+            % endif
             "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Debug/grpc_csharp_ext.dll",
             "grpc_csharp_ext.x86.dll": "../../../vsprojects/Debug/grpc_csharp_ext.dll",
             "libgrpc_csharp_ext.x64.so": "../../../libs/dbg/libgrpc_csharp_ext.so",
@@ -38,10 +40,12 @@
           "includeFiles": [ "../Grpc.Core/Version.cs" ]
         },
         "copyToOutput": {
-          % if includeData:
-          "include": "data/*",
-          % endif
           "mappings": {
+            % if includeData:
+            "data/ca.pem": "../Grpc.IntegrationTesting/data/ca.pem",
+            "data/server1.key": "../Grpc.IntegrationTesting/data/server1.key",
+            "data/server1.pem": "../Grpc.IntegrationTesting/data/server1.pem",
+            % endif
             "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Release/grpc_csharp_ext.dll",
             "grpc_csharp_ext.x86.dll": "../../../vsprojects/Release/grpc_csharp_ext.dll",
             "libgrpc_csharp_ext.x64.so": "../../../libs/opt/libgrpc_csharp_ext.so",
@@ -51,9 +55,4 @@
       }
     }
   },
-  "runtimes": {
-    "win7-x64": { },
-    "debian.8-x64": { },
-    "osx.10.11-x64": { }
-  },
-  % endif
\ No newline at end of file
+  %endif
diff --git a/templates/src/csharp/build_packages.bat.template b/templates/src/csharp/build_packages.bat.template
index ea2acb6..87c4b5a 100644
--- a/templates/src/csharp/build_packages.bat.template
+++ b/templates/src/csharp/build_packages.bat.template
@@ -31,12 +31,13 @@
   
   @rem Builds gRPC NuGet packages
   
+  @rem This way of building nuget packages is now obsolete. C# nuget packages
+  @rem with CoreCLR support are now being built using the dotnet cli
+  @rem in build_packages_dotnetcli.sh
+  
   @rem Current package versions
   set VERSION=${settings.csharp_version}
-  set PROTOBUF_VERSION=3.0.0-beta3
-  
-  @rem Packages that depend on prerelease packages (like Google.Protobuf) need to have prerelease suffix as well.
-  set VERSION_WITH_BETA=%VERSION%-beta
+  set PROTOBUF_VERSION=3.0.0
   
   @rem Adjust the location of nuget.exe
   set NUGET=C:\nuget\nuget.exe
@@ -60,7 +61,6 @@
   
   @rem Fetch all dependencies
   %%NUGET% restore ..\..\vsprojects\grpc_csharp_ext.sln || goto :error
-  %%NUGET% restore Grpc.sln || goto :error
   
   setlocal
   
@@ -75,7 +75,7 @@
   
   %%NUGET% pack Grpc.Auth\Grpc.Auth.nuspec -Symbols -Version %VERSION% || goto :error
   %%NUGET% pack Grpc.Core\Grpc.Core.nuspec -Symbols -Version %VERSION% || goto :error
-  %%NUGET% pack Grpc.HealthCheck\Grpc.HealthCheck.nuspec -Symbols -Version %VERSION_WITH_BETA% -Properties ProtobufVersion=%PROTOBUF_VERSION% || goto :error
+  %%NUGET% pack Grpc.HealthCheck\Grpc.HealthCheck.nuspec -Symbols -Version %VERSION% -Properties ProtobufVersion=%PROTOBUF_VERSION% || goto :error
   %%NUGET% pack Grpc.nuspec -Version %VERSION% || goto :error
   %%NUGET% pack Grpc.Tools.nuspec -Version %VERSION% || goto :error
   
@@ -83,8 +83,8 @@
   xcopy /Y /I *.nupkg ..\..\artifacts${"\\"}
   
   @rem create a zipfile with the artifacts as well
-  powershell -Command "Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('..\..\artifacts', 'csharp_nugets.zip');"
-  xcopy /Y /I csharp_nugets.zip ..\..\artifacts${"\\"}
+  powershell -Command "Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('..\..\artifacts', 'csharp_nugets_obsolete.zip');"
+  xcopy /Y /I csharp_nugets_obsolete.zip ..\..\artifacts${"\\"}
   
   goto :EOF
   
diff --git a/templates/src/csharp/build_packages_dotnetcli.sh.template b/templates/src/csharp/build_packages_dotnetcli.sh.template
new file mode 100755
index 0000000..99b049a
--- /dev/null
+++ b/templates/src/csharp/build_packages_dotnetcli.sh.template
@@ -0,0 +1,72 @@
+%YAML 1.2
+--- |
+  #!/bin/bash
+  # Copyright 2016, Google Inc.
+  # All rights reserved.
+  #
+  # Redistribution and use in source and binary forms, with or without
+  # modification, are permitted provided that the following conditions are
+  # met:
+  #
+  #     * Redistributions of source code must retain the above copyright
+  # notice, this list of conditions and the following disclaimer.
+  #     * Redistributions in binary form must reproduce the above
+  # copyright notice, this list of conditions and the following disclaimer
+  # in the documentation and/or other materials provided with the
+  # distribution.
+  #     * Neither the name of Google Inc. nor the names of its
+  # contributors may be used to endorse or promote products derived from
+  # this software without specific prior written permission.
+  #
+  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  
+  set -ex
+  
+  cd $(dirname $0)
+  
+  mkdir -p ../../artifacts/
+  
+  mkdir -p nativelibs/windows_x86 nativelibs/windows_x64 ${"\\"}
+      nativelibs/linux_x86 nativelibs/linux_x64 ${"\\"}
+      nativelibs/macosx_x86 nativelibs/macosx_x64
+  
+  mkdir -p protoc_plugins/windows_x86 protoc_plugins/windows_x64 ${"\\"}
+      protoc_plugins/linux_x86 protoc_plugins/linux_x64 ${"\\"}
+      protoc_plugins/macosx_x86 protoc_plugins/macosx_x64
+  
+  # Collect the artifacts built by the previous build step if running on Jenkins
+  cp $EXTERNAL_GIT_ROOT/architecture=x86,language=csharp,platform=windows/artifacts/* nativelibs/windows_x86 || true
+  cp $EXTERNAL_GIT_ROOT/architecture=x64,language=csharp,platform=windows/artifacts/* nativelibs/windows_x64 || true
+  cp $EXTERNAL_GIT_ROOT/architecture=x86,language=csharp,platform=linux/artifacts/* nativelibs/linux_x86 || true
+  cp $EXTERNAL_GIT_ROOT/architecture=x64,language=csharp,platform=linux/artifacts/* nativelibs/linux_x64 || true
+  cp $EXTERNAL_GIT_ROOT/architecture=x86,language=csharp,platform=macos/artifacts/* nativelibs/macosx_x86 || true
+  cp $EXTERNAL_GIT_ROOT/architecture=x64,language=csharp,platform=macos/artifacts/* nativelibs/macosx_x64 || true
+  
+  # Collect protoc artifacts built by the previous build step
+  cp $EXTERNAL_GIT_ROOT/architecture=x86,language=protoc,platform=windows/artifacts/* protoc_plugins/windows_x86 || true
+  cp $EXTERNAL_GIT_ROOT/architecture=x64,language=protoc,platform=windows/artifacts/* protoc_plugins/windows_x64 || true
+  cp $EXTERNAL_GIT_ROOT/architecture=x86,language=protoc,platform=linux/artifacts/* protoc_plugins/linux_x86 || true
+  cp $EXTERNAL_GIT_ROOT/architecture=x64,language=protoc,platform=linux/artifacts/* protoc_plugins/linux_x64 || true
+  cp $EXTERNAL_GIT_ROOT/architecture=x86,language=protoc,platform=macos/artifacts/* protoc_plugins/macosx_x86 || true
+  cp $EXTERNAL_GIT_ROOT/architecture=x64,language=protoc,platform=macos/artifacts/* protoc_plugins/macosx_x64 || true
+  
+  dotnet restore .
+  
+  dotnet pack --configuration Release Grpc.Core/project.json --output ../../artifacts
+  dotnet pack --configuration Release Grpc.Auth/project.json --output ../../artifacts
+  dotnet pack --configuration Release Grpc.HealthCheck/project.json --output ../../artifacts
+  
+  nuget pack Grpc.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../artifacts
+  nuget pack Grpc.Tools.nuspec -Version "${settings.csharp_version}" -OutputDirectory ../../artifacts
+  
+  (cd ../../artifacts && zip csharp_nugets_dotnetcli.zip *.nupkg)
diff --git a/templates/src/node/health_check/package.json.template b/templates/src/node/health_check/package.json.template
index 96b9748..c2bb232 100644
--- a/templates/src/node/health_check/package.json.template
+++ b/templates/src/node/health_check/package.json.template
@@ -17,9 +17,9 @@
       }
     ],
     "dependencies": {
-      "grpc": "^0.15.0",
+      "grpc": "^${settings.node_version}",
       "lodash": "^3.9.3",
-      "google-protobuf": "^3.0.0-alpha.5"
+      "google-protobuf": "^3.0.0"
     },
     "files": [
       "LICENSE",
diff --git a/templates/src/php/composer.json.template b/templates/src/php/composer.json.template
new file mode 100644
index 0000000..7feeae9
--- /dev/null
+++ b/templates/src/php/composer.json.template
@@ -0,0 +1,23 @@
+%YAML 1.2
+--- |
+  {
+    "name": "grpc/grpc",
+    "type": "library",
+    "description": "gRPC library for PHP",
+    "keywords": ["rpc"],
+    "homepage": "http://grpc.io",
+    "license": "BSD-3-Clause",
+    "version": "${settings.php_version.php_composer()}",
+    "require": {
+      "php": ">=5.5.0",
+      "google/protobuf": "v3.1.0-alpha-1"
+    },
+    "require-dev": {
+      "google/auth": "v0.9"
+    },
+    "autoload": {
+      "psr-4": {
+        "Grpc\\": "lib/Grpc/"
+      }
+    }
+  }
diff --git a/templates/tools/dockerfile/apt_get_pyenv.include b/templates/tools/dockerfile/apt_get_pyenv.include
index 70e9028..816b279 100644
--- a/templates/tools/dockerfile/apt_get_pyenv.include
+++ b/templates/tools/dockerfile/apt_get_pyenv.include
@@ -15,4 +15,5 @@
 RUN pyenv update
 RUN pyenv install 3.5-dev
 RUN pyenv install 3.6-dev
-RUN pyenv local 3.5-dev 3.6-dev
+RUN pyenv install pypy-5.3.1
+RUN pyenv local 3.5-dev 3.6-dev pypy-5.3.1
diff --git a/templates/tools/dockerfile/csharp_deps.include b/templates/tools/dockerfile/csharp_deps.include
index 489dc44..7e89dec 100644
--- a/templates/tools/dockerfile/csharp_deps.include
+++ b/templates/tools/dockerfile/csharp_deps.include
@@ -14,3 +14,5 @@
     ca-certificates-mono ${'\\'}
     nuget ${'\\'}
     && apt-get clean
+
+RUN nuget update -self
diff --git a/templates/tools/dockerfile/csharp_dotnetcli_deps.include b/templates/tools/dockerfile/csharp_dotnetcli_deps.include
new file mode 100644
index 0000000..430f3fa
--- /dev/null
+++ b/templates/tools/dockerfile/csharp_dotnetcli_deps.include
@@ -0,0 +1,13 @@
+# Install dotnet SDK based on https://www.microsoft.com/net/core#debian
+RUN apt-get update && apt-get install -y curl libunwind8 gettext
+RUN curl -sSL -o dotnet.tar.gz https://go.microsoft.com/fwlink/?LinkID=809130
+RUN mkdir -p /opt/dotnet && tar zxf dotnet.tar.gz -C /opt/dotnet
+RUN ln -s /opt/dotnet/dotnet /usr/local/bin
+
+# Trigger the population of the local package cache
+ENV NUGET_XMLDOC_MODE skip
+RUN mkdir warmup ${'\\'}
+    && cd warmup ${'\\'}
+    && dotnet new ${'\\'}
+    && cd .. ${'\\'}
+    && rm -rf warmup
diff --git a/templates/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile.template b/templates/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile.template
new file mode 100644
index 0000000..092f04d
--- /dev/null
+++ b/templates/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile.template
@@ -0,0 +1,41 @@
+%YAML 1.2
+--- |
+  # Copyright 2015, Google Inc.
+  # All rights reserved.
+  #
+  # Redistribution and use in source and binary forms, with or without
+  # modification, are permitted provided that the following conditions are
+  # met:
+  #
+  #     * Redistributions of source code must retain the above copyright
+  # notice, this list of conditions and the following disclaimer.
+  #     * Redistributions in binary form must reproduce the above
+  # copyright notice, this list of conditions and the following disclaimer
+  # in the documentation and/or other materials provided with the
+  # distribution.
+  #     * Neither the name of Google Inc. nor the names of its
+  # contributors may be used to endorse or promote products derived from
+  # this software without specific prior written permission.
+  #
+  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  
+  FROM debian:jessie
+  
+  <%include file="../../apt_get_basic.include"/>
+  <%include file="../../python_deps.include"/>
+  <%include file="../../csharp_deps.include"/>
+  <%include file="../../csharp_dotnetcli_deps.include"/>
+  <%include file="../../run_tests_addons.include"/>
+  # Define the default command.
+  CMD ["bash"]
+  
diff --git a/templates/tools/dockerfile/interoptest/grpc_interop_php/Dockerfile.template b/templates/tools/dockerfile/interoptest/grpc_interop_php/Dockerfile.template
index f37eada..9689f67 100644
--- a/templates/tools/dockerfile/interoptest/grpc_interop_php/Dockerfile.template
+++ b/templates/tools/dockerfile/interoptest/grpc_interop_php/Dockerfile.template
@@ -32,7 +32,6 @@
   FROM debian:jessie
   
   <%include file="../../apt_get_basic.include"/>
-  <%include file="../../ruby_deps.include"/>
   <%include file="../../php_deps.include"/>
   <%include file="../../run_tests_addons.include"/>
   <%include file="../../php_common_deps.include"/>
diff --git a/templates/tools/dockerfile/interoptest/grpc_interop_php7/Dockerfile.template b/templates/tools/dockerfile/interoptest/grpc_interop_php7/Dockerfile.template
index 42157ee..c557f34 100644
--- a/templates/tools/dockerfile/interoptest/grpc_interop_php7/Dockerfile.template
+++ b/templates/tools/dockerfile/interoptest/grpc_interop_php7/Dockerfile.template
@@ -32,6 +32,5 @@
   FROM debian:jessie
   
   <%include file="../../php7_deps.include"/>
-  <%include file="../../ruby_deps.include"/>
   <%include file="../../run_tests_addons.include"/>
   <%include file="../../php_common_deps.include"/>
diff --git a/templates/tools/dockerfile/php_common_deps.include b/templates/tools/dockerfile/php_common_deps.include
index 8839bb5..88a90f8 100644
--- a/templates/tools/dockerfile/php_common_deps.include
+++ b/templates/tools/dockerfile/php_common_deps.include
@@ -1,21 +1,6 @@
-# ronn: a ruby tool used to convert markdown to man pages, used during the
-# install of Protobuf extensions
-#
-# rake: a ruby version of make used to build the PHP Protobuf extension
-RUN /bin/bash -l -c "rvm all do gem install ronn rake"
-
 # Install composer
 RUN curl -sS https://getcomposer.org/installer | php
 RUN mv composer.phar /usr/local/bin/composer
 
-# Download the patched PHP protobuf so that PHP gRPC clients can be generated
-# from proto3 schemas.
-RUN git clone https://github.com/stanley-cheung/Protobuf-PHP.git /var/local/git/protobuf-php
-
-RUN /bin/bash -l -c "rvm use ruby-2.1 ${'\\'}
-  && cd /var/local/git/protobuf-php ${'\\'}
-  && rvm all do rake pear:package version=1.0 ${'\\'}
-  && pear install Protobuf-1.0.tgz"
-
 # Define the default command.
 CMD ["bash"]
diff --git a/templates/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile.template b/templates/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile.template
index 13a250e..49ba601 100644
--- a/templates/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile.template
+++ b/templates/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile.template
@@ -37,25 +37,10 @@
   <%include file="../../gcp_api_libraries.include"/>
   <%include file="../../php_deps.include"/>
   <%include file="../../run_tests_addons.include"/>
-  # ronn: a ruby tool used to convert markdown to man pages, used during the
-  # install of Protobuf extensions
-  #
-  # rake: a ruby version of make used to build the PHP Protobuf extension
-  RUN /bin/bash -l -c "rvm all do gem install ronn rake"
-  
   # Install composer
   RUN curl -sS https://getcomposer.org/installer | php
   RUN mv composer.phar /usr/local/bin/composer
   
-  # Download the patched PHP protobuf so that PHP gRPC clients can be generated
-  # from proto3 schemas.
-  RUN git clone https://github.com/stanley-cheung/Protobuf-PHP.git /var/local/git/protobuf-php
-  
-  RUN /bin/bash -l -c "rvm use ruby-2.1 ${'\\'}
-    && cd /var/local/git/protobuf-php ${'\\'}
-    && rvm all do rake pear:package version=1.0 ${'\\'}
-    && pear install Protobuf-1.0.tgz"
-  
   # Define the default command.
   CMD ["bash"]
   
diff --git a/templates/tools/dockerfile/test/csharp_coreclr_x64/Dockerfile.template b/templates/tools/dockerfile/test/csharp_coreclr_x64/Dockerfile.template
index 24dad48..092f04d 100644
--- a/templates/tools/dockerfile/test/csharp_coreclr_x64/Dockerfile.template
+++ b/templates/tools/dockerfile/test/csharp_coreclr_x64/Dockerfile.template
@@ -34,21 +34,7 @@
   <%include file="../../apt_get_basic.include"/>
   <%include file="../../python_deps.include"/>
   <%include file="../../csharp_deps.include"/>
-  
-  # Install dotnet SDK based on https://www.microsoft.com/net/core#debian
-  RUN apt-get update && apt-get install -y curl libunwind8 gettext
-  RUN curl -sSL -o dotnet.tar.gz https://go.microsoft.com/fwlink/?LinkID=809130
-  RUN mkdir -p /opt/dotnet && tar zxf dotnet.tar.gz -C /opt/dotnet
-  RUN ln -s /opt/dotnet/dotnet /usr/local/bin
-  
-  # Trigger the population of the local package cache
-  ENV NUGET_XMLDOC_MODE skip
-  RUN mkdir warmup ${'\\'}
-      && cd warmup ${'\\'}
-      && dotnet new ${'\\'}
-      && cd .. ${'\\'}
-      && rm -rf warmup
-  
+  <%include file="../../csharp_dotnetcli_deps.include"/>
   <%include file="../../run_tests_addons.include"/>
   # Define the default command.
   CMD ["bash"]
diff --git a/templates/tools/run_tests/sources_and_headers.json.template b/templates/tools/run_tests/sources_and_headers.json.template
index 0755982..1c5c974 100644
--- a/templates/tools/run_tests/sources_and_headers.json.template
+++ b/templates/tools/run_tests/sources_and_headers.json.template
@@ -37,6 +37,7 @@
 
   ${json.dumps([{"name": tgt.name,
                  "type": typ,
+                 "is_filegroup": False,
                  "language": tgt.language,
                  "third_party": tgt.boringssl or tgt.zlib,
                  "src": sorted(
@@ -50,5 +51,22 @@
                  "deps": sorted(tgt.get('deps', []) +
                                 tgt.get('uses', []) +
                                 tgt.get('filegroups', []))}
-                for typ, tgt in all_targets(targets, libs, filegroups)],
+                for typ, tgt in all_targets(targets, libs, [])] +
+                [{"name": tgt.name,
+                 "type": typ,
+                 "is_filegroup": True,
+                 "language": tgt.language,
+                 "third_party": tgt.boringssl or tgt.zlib,
+                 "src": sorted(
+                     filter_srcs(tgt.own_src, (no_protos_filter, no_third_party_filter)) +
+                     filter_srcs(tgt.own_public_headers, (no_protos_filter, no_third_party_filter)) +
+                     filter_srcs(tgt.own_headers, (no_third_party_filter,))),
+                 "headers": sorted(
+                     tgt.own_public_headers +
+                     tgt.own_headers +
+                     proto_headers(tgt.own_src)),
+                 "deps": sorted(tgt.get('deps', []) +
+                                tgt.get('uses', []) +
+                                tgt.get('filegroups', []))}
+                for typ, tgt in all_targets([], [], filegroups)],
                sort_keys=True, indent=2)}
diff --git a/test/core/bad_client/bad_client.c b/test/core/bad_client/bad_client.c
index 24ee338..be88d4a 100644
--- a/test/core/bad_client/bad_client.c
+++ b/test/core/bad_client/bad_client.c
@@ -130,7 +130,7 @@
   grpc_server_start(a.server);
   transport = grpc_create_chttp2_transport(&exec_ctx, NULL, sfd.server, 0);
   server_setup_transport(&a, transport);
-  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
   grpc_exec_ctx_finish(&exec_ctx);
 
   /* Bind everything into the same pollset */
diff --git a/test/core/bad_client/tests/head_of_line_blocking.c b/test/core/bad_client/tests/head_of_line_blocking.c
index e4051bb..64cb79d 100644
--- a/test/core/bad_client/tests/head_of_line_blocking.c
+++ b/test/core/bad_client/tests/head_of_line_blocking.c
@@ -97,7 +97,7 @@
                                               &deadline, &request_metadata_recv,
                                               &payload, cq, cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(payload != NULL);
diff --git a/test/core/bad_client/tests/large_metadata.c b/test/core/bad_client/tests/large_metadata.c
index ded5f17..d106ce6 100644
--- a/test/core/bad_client/tests/large_metadata.c
+++ b/test/core/bad_client/tests/large_metadata.c
@@ -121,7 +121,7 @@
   error = grpc_server_request_call(server, &s, &call_details,
                                    &request_metadata_recv, cq, cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(0 == strcmp(call_details.host, "localhost"));
@@ -148,7 +148,7 @@
   error = grpc_server_request_call(server, &s, &call_details,
                                    &request_metadata_recv, cq, cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(0 == strcmp(call_details.host, "localhost"));
@@ -171,7 +171,7 @@
   op.reserved = NULL;
   error = grpc_call_start_batch(s, &op, 1, tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(102), 0);  // Operation fails.
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 0);  // Operation fails.
   cq_verify(cqv);
 
   gpr_free((char *)meta.value);
diff --git a/test/core/bad_client/tests/server_registered_method.c b/test/core/bad_client/tests/server_registered_method.c
index 6216553..e174af5 100644
--- a/test/core/bad_client/tests/server_registered_method.c
+++ b/test/core/bad_client/tests/server_registered_method.c
@@ -70,7 +70,7 @@
                                               &deadline, &request_metadata_recv,
                                               &payload, cq, cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(payload != NULL);
@@ -96,7 +96,7 @@
                                               &deadline, &request_metadata_recv,
                                               &payload, cq, cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(payload == NULL);
diff --git a/test/core/bad_client/tests/simple_request.c b/test/core/bad_client/tests/simple_request.c
index 25bbe96..c08aa40 100644
--- a/test/core/bad_client/tests/simple_request.c
+++ b/test/core/bad_client/tests/simple_request.c
@@ -114,7 +114,7 @@
   error = grpc_server_request_call(server, &s, &call_details,
                                    &request_metadata_recv, cq, cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(0 == strcmp(call_details.host, "localhost"));
diff --git a/test/core/bad_ssl/bad_ssl_test.c b/test/core/bad_ssl/bad_ssl_test.c
index bb06ab0..c9cdb16 100644
--- a/test/core/bad_ssl/bad_ssl_test.c
+++ b/test/core/bad_ssl/bad_ssl_test.c
@@ -111,7 +111,7 @@
   error = grpc_call_start_batch(c, ops, nops, tag(1), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status != GRPC_STATUS_OK);
diff --git a/test/core/bad_ssl/gen_build_yaml.py b/test/core/bad_ssl/gen_build_yaml.py
index 69f9219..c17b17e 100755
--- a/test/core/bad_ssl/gen_build_yaml.py
+++ b/test/core/bad_ssl/gen_build_yaml.py
@@ -41,7 +41,8 @@
 # maps test names to options
 BAD_CLIENT_TESTS = {
     'cert': default_test_options._replace(cpu_cost=0.1),
-    'alpn': default_test_options._replace(cpu_cost=0.1),
+    # Disabling this test because it does not link correctly as written
+    # 'alpn': default_test_options._replace(cpu_cost=0.1),
 }
 
 def main():
diff --git a/test/core/census/README b/test/core/census/README
new file mode 100644
index 0000000..d5363b7
--- /dev/null
+++ b/test/core/census/README
@@ -0,0 +1,7 @@
+Test source and data files for Census.
+
+binary proto files (*.pb) in data directory are generated from the *.txt file,
+via:
+
+BASE="filename"
+cat $BASE.txt | protoc --encode=google.census.Resource census.proto > $BASE.pb
diff --git a/test/core/census/data/context_empty.pb b/test/core/census/data/context_empty.pb
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/core/census/data/context_empty.pb
diff --git a/test/core/census/data/context_empty.txt b/test/core/census/data/context_empty.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/core/census/data/context_empty.txt
diff --git a/test/core/census/data/context_full.pb b/test/core/census/data/context_full.pb
new file mode 100644
index 0000000..80ebcf2
--- /dev/null
+++ b/test/core/census/data/context_full.pb
Binary files differ
diff --git a/test/core/census/data/context_full.txt b/test/core/census/data/context_full.txt
new file mode 100644
index 0000000..7901a10
--- /dev/null
+++ b/test/core/census/data/context_full.txt
@@ -0,0 +1,3 @@
+trace_id { hi : 5; lo : 1 }
+span_id : 7
+is_sampled : true
diff --git a/test/core/census/data/context_no_sample.pb b/test/core/census/data/context_no_sample.pb
new file mode 100644
index 0000000..ab7ad7d
--- /dev/null
+++ b/test/core/census/data/context_no_sample.pb
Binary files differ
diff --git a/test/core/census/data/context_no_sample.txt b/test/core/census/data/context_no_sample.txt
new file mode 100644
index 0000000..1502980
--- /dev/null
+++ b/test/core/census/data/context_no_sample.txt
@@ -0,0 +1,2 @@
+trace_id { hi : 5; lo : 1 }
+span_id : 7
diff --git a/test/core/census/data/context_span_only.pb b/test/core/census/data/context_span_only.pb
new file mode 100644
index 0000000..2a9527a
--- /dev/null
+++ b/test/core/census/data/context_span_only.pb
Binary files differ
diff --git a/test/core/census/data/context_span_only.txt b/test/core/census/data/context_span_only.txt
new file mode 100644
index 0000000..d90de2e
--- /dev/null
+++ b/test/core/census/data/context_span_only.txt
@@ -0,0 +1,2 @@
+span_id : 7
+is_sampled : true
diff --git a/test/core/census/data/context_trace_only.pb b/test/core/census/data/context_trace_only.pb
new file mode 100644
index 0000000..7fdf6f6
--- /dev/null
+++ b/test/core/census/data/context_trace_only.pb
Binary files differ
diff --git a/test/core/census/data/context_trace_only.txt b/test/core/census/data/context_trace_only.txt
new file mode 100644
index 0000000..9b68a6a
--- /dev/null
+++ b/test/core/census/data/context_trace_only.txt
@@ -0,0 +1,2 @@
+trace_id { hi : 5; lo : 1 }
+is_sampled : true
diff --git a/test/core/census/data/resource_empty_name.pb b/test/core/census/data/resource_empty_name.pb
new file mode 100644
index 0000000..4d54744
--- /dev/null
+++ b/test/core/census/data/resource_empty_name.pb
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/core/census/data/resource_empty_name.txt b/test/core/census/data/resource_empty_name.txt
new file mode 100644
index 0000000..271fd32
--- /dev/null
+++ b/test/core/census/data/resource_empty_name.txt
@@ -0,0 +1,5 @@
+# Name is present, but empty.
+name : ''
+unit {
+  numerator : SECS
+}
diff --git a/test/core/census/data/resource_full.pb b/test/core/census/data/resource_full.pb
new file mode 100644
index 0000000..e4c6a2a
--- /dev/null
+++ b/test/core/census/data/resource_full.pb
Binary files differ
diff --git a/test/core/census/data/resource_full.txt b/test/core/census/data/resource_full.txt
new file mode 100644
index 0000000..1aa2faf
--- /dev/null
+++ b/test/core/census/data/resource_full.txt
@@ -0,0 +1,9 @@
+# A full resource definition - all fields filled out.
+name : 'full_resource'
+description : 'A resource with everything defined'
+unit {
+  # Megabits per second.
+  prefix : 6
+  numerator : BITS
+  denominator : SECS
+}
diff --git a/test/core/census/data/resource_minimal_good.pb b/test/core/census/data/resource_minimal_good.pb
new file mode 100644
index 0000000..7100c46
--- /dev/null
+++ b/test/core/census/data/resource_minimal_good.pb
@@ -0,0 +1,2 @@
+
+minimal_good
\ No newline at end of file
diff --git a/test/core/census/data/resource_minimal_good.txt b/test/core/census/data/resource_minimal_good.txt
new file mode 100644
index 0000000..a7a7e71
--- /dev/null
+++ b/test/core/census/data/resource_minimal_good.txt
@@ -0,0 +1,5 @@
+# A minimal "good" Resource definition: has a name and numerator/unit.
+name : 'minimal_good'
+unit {
+  numerator : SECS
+}
diff --git a/test/core/census/data/resource_no_name.pb b/test/core/census/data/resource_no_name.pb
new file mode 100644
index 0000000..4d54744
--- /dev/null
+++ b/test/core/census/data/resource_no_name.pb
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/core/census/data/resource_no_name.txt b/test/core/census/data/resource_no_name.txt
new file mode 100644
index 0000000..8f12a91
--- /dev/null
+++ b/test/core/census/data/resource_no_name.txt
@@ -0,0 +1,4 @@
+# The minimal good Resource without a name.
+unit {
+  numerator : SECS
+}
diff --git a/test/core/census/data/resource_no_numerator.pb b/test/core/census/data/resource_no_numerator.pb
new file mode 100644
index 0000000..2a5ccee
--- /dev/null
+++ b/test/core/census/data/resource_no_numerator.pb
@@ -0,0 +1,2 @@
+
+resource_no_numeratorýÿÿÿÿÿÿÿÿ
\ No newline at end of file
diff --git a/test/core/census/data/resource_no_numerator.txt b/test/core/census/data/resource_no_numerator.txt
new file mode 100644
index 0000000..fc1fec7
--- /dev/null
+++ b/test/core/census/data/resource_no_numerator.txt
@@ -0,0 +1,6 @@
+# Resource without a numerator
+name : 'resource_no_numerator'
+unit {
+  prefix : -3
+  denominator : SECS
+}
diff --git a/test/core/census/data/resource_no_unit.pb b/test/core/census/data/resource_no_unit.pb
new file mode 100644
index 0000000..9dca262
--- /dev/null
+++ b/test/core/census/data/resource_no_unit.pb
@@ -0,0 +1,2 @@
+
+resource_no_unit
\ No newline at end of file
diff --git a/test/core/census/data/resource_no_unit.txt b/test/core/census/data/resource_no_unit.txt
new file mode 100644
index 0000000..c5d5115
--- /dev/null
+++ b/test/core/census/data/resource_no_unit.txt
@@ -0,0 +1,2 @@
+# The minimal good resource without a unit
+name : 'resource_no_unit'
diff --git a/test/core/census/resource_test.c b/test/core/census/resource_test.c
new file mode 100644
index 0000000..f0e7039
--- /dev/null
+++ b/test/core/census/resource_test.c
@@ -0,0 +1,169 @@
+/*
+ *
+ * Copyright 2016, 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/ext/census/resource.h"
+#include <grpc/census.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/useful.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "src/core/ext/census/base_resources.h"
+#include "test/core/util/test_config.h"
+
+// Test all the functionality for dealing with Resources.
+
+// Just startup and shutdown resources subsystem.
+static void test_enable_disable() {
+  initialize_resources();
+  shutdown_resources();
+}
+
+// A blank/empty initialization should not work.
+static void test_empty_definition() {
+  initialize_resources();
+  int32_t rid = census_define_resource(NULL, 0);
+  GPR_ASSERT(rid == -1);
+  uint8_t buffer[50] = {0};
+  rid = census_define_resource(buffer, 50);
+  GPR_ASSERT(rid == -1);
+  shutdown_resources();
+}
+
+// Given a file name, read raw proto and define the resource included within.
+// Returns resource id from census_define_resource().
+static int32_t define_resource_from_file(const char *file) {
+#define BUF_SIZE 512
+  uint8_t buffer[BUF_SIZE];
+  FILE *input = fopen(file, "rb");
+  GPR_ASSERT(input != NULL);
+  size_t nbytes = fread(buffer, 1, BUF_SIZE, input);
+  GPR_ASSERT(nbytes != 0 && nbytes < BUF_SIZE && feof(input) && !ferror(input));
+  int32_t rid = census_define_resource(buffer, nbytes);
+  GPR_ASSERT(fclose(input) == 0);
+  return rid;
+}
+
+// Test definition of a single resource, using a proto read from a file. The
+// `succeed` parameter indicates whether we expect the definition to succeed or
+// fail. `name` is used to check that the returned resource can be looked up by
+// name.
+static void test_define_single_resource(const char *file, const char *name,
+                                        bool succeed) {
+  gpr_log(GPR_INFO, "Test defining resource \"%s\"\n", name);
+  initialize_resources();
+  int32_t rid = define_resource_from_file(file);
+  if (succeed) {
+    GPR_ASSERT(rid >= 0);
+    int32_t rid2 = census_resource_id(name);
+    GPR_ASSERT(rid == rid2);
+  } else {
+    GPR_ASSERT(rid < 0);
+  }
+  shutdown_resources();
+}
+
+// Try deleting various resources (both those that exist and those that don't).
+static void test_delete_resource(const char *minimal_good, const char *full) {
+  initialize_resources();
+  // Try deleting resource before any are defined.
+  census_delete_resource(0);
+  // Create and check a couple of resources.
+  int32_t rid1 = define_resource_from_file(minimal_good);
+  int32_t rid2 = define_resource_from_file(full);
+  GPR_ASSERT(rid1 >= 0 && rid2 >= 0 && rid1 != rid2);
+  int32_t rid3 = census_resource_id("minimal_good");
+  int32_t rid4 = census_resource_id("full_resource");
+  GPR_ASSERT(rid1 == rid3 && rid2 == rid4);
+  // Try deleting non-existant resources.
+  census_delete_resource(-1);
+  census_delete_resource(rid1 + rid2 + 1);
+  census_delete_resource(10000000);
+  // Delete one of the previously defined resources and check for deletion.
+  census_delete_resource(rid1);
+  rid3 = census_resource_id("minimal_good");
+  GPR_ASSERT(rid3 < 0);
+  // Check that re-adding works.
+  rid1 = define_resource_from_file(minimal_good);
+  GPR_ASSERT(rid1 >= 0);
+  rid3 = census_resource_id("minimal_good");
+  GPR_ASSERT(rid1 == rid3);
+  shutdown_resources();
+}
+
+// Test define base resources.
+static void test_base_resources() {
+  initialize_resources();
+  define_base_resources();
+  int32_t rid1 = census_resource_id("client_rpc_latency");
+  int32_t rid2 = census_resource_id("server_rpc_latency");
+  GPR_ASSERT(rid1 >= 0 && rid2 >= 0 && rid1 != rid2);
+  shutdown_resources();
+}
+
+int main(int argc, char **argv) {
+  const char *resource_empty_name_pb, *resource_full_pb,
+      *resource_minimal_good_pb, *resource_no_name_pb,
+      *resource_no_numerator_pb, *resource_no_unit_pb;
+  if (argc == 7) {
+    resource_empty_name_pb = argv[1];
+    resource_full_pb = argv[2];
+    resource_minimal_good_pb = argv[3];
+    resource_no_name_pb = argv[4];
+    resource_no_numerator_pb = argv[5];
+    resource_no_unit_pb = argv[6];
+  } else {
+    GPR_ASSERT(argc == 1);
+    resource_empty_name_pb = "test/core/census/data/resource_empty_name.pb";
+    resource_full_pb = "test/core/census/data/resource_full.pb";
+    resource_minimal_good_pb = "test/core/census/data/resource_minimal_good.pb";
+    resource_no_name_pb = "test/core/census/data/resource_no_name.pb";
+    resource_no_numerator_pb = "test/core/census/data/resource_no_numerator.pb";
+    resource_no_unit_pb = "test/core/census/data/resource_no_unit.pb";
+  }
+  grpc_test_init(argc, argv);
+  test_enable_disable();
+  test_empty_definition();
+  test_define_single_resource(resource_minimal_good_pb, "minimal_good", true);
+  test_define_single_resource(resource_full_pb, "full_resource", true);
+  test_define_single_resource(resource_no_name_pb, "resource_no_name", false);
+  test_define_single_resource(resource_no_numerator_pb, "resource_no_numerator",
+                              false);
+  test_define_single_resource(resource_no_unit_pb, "resource_no_unit", false);
+  test_define_single_resource(resource_empty_name_pb, "resource_empty_name",
+                              false);
+  test_delete_resource(resource_minimal_good_pb, resource_full_pb);
+  test_base_resources();
+  return 0;
+}
diff --git a/test/core/census/trace_context_test.c b/test/core/census/trace_context_test.c
new file mode 100644
index 0000000..ee409e8
--- /dev/null
+++ b/test/core/census/trace_context_test.c
@@ -0,0 +1,231 @@
+/*
+ *
+ * Copyright 2016, 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/census.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/useful.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "src/core/ext/census/base_resources.h"
+#include "src/core/ext/census/resource.h"
+#include "test/core/util/test_config.h"
+
+#include "src/core/ext/census/gen/trace_context.pb.h"
+#include "src/core/ext/census/trace_context.h"
+#include "third_party/nanopb/pb_decode.h"
+#include "third_party/nanopb/pb_encode.h"
+
+#define BUF_SIZE 256
+
+/* Encodes a TraceContext structure (ctxt1) to a buffer, and then decodes it
+to a second TraceContext (ctxt2).  Validates that the resulting TraceContext
+has a span_id, trace_id, and that the values are equal to those in initial
+TraceContext. On success, returns true.  If encode_trace_context returns 0,
+decode_trace_context fails, or the resulting TraceContext is missing a trace_id
+or span_id, it will return false. */
+bool validate_encode_decode_context(google_trace_TraceContext *ctxt1,
+                                    uint8_t *buffer, size_t buf_size) {
+  google_trace_TraceContext ctxt2 = google_trace_TraceContext_init_zero;
+  size_t msg_length;
+  GPR_ASSERT(ctxt1->has_trace_id && ctxt1->has_span_id);
+
+  msg_length = encode_trace_context(ctxt1, buffer, buf_size);
+  if (msg_length == 0) {
+    return false;
+  }
+
+  if (!decode_trace_context(&ctxt2, buffer, msg_length)) {
+    return false;
+  }
+
+  if (!ctxt2.has_trace_id || !ctxt2.has_span_id) {
+    return false;
+  }
+
+  GPR_ASSERT(
+      ctxt1->trace_id.hi == ctxt2.trace_id.hi &&
+      ctxt1->trace_id.lo == ctxt2.trace_id.lo &&
+      ctxt1->span_id == ctxt2.span_id &&
+      ctxt1->has_is_sampled == ctxt2.has_is_sampled &&
+      (ctxt1->has_is_sampled ? ctxt1->is_sampled == ctxt2.is_sampled : true));
+
+  return true;
+}
+
+/* Decodes a proto-encoded TraceContext from a buffer.  If decode_trace_context
+fails or the resulting TraceContext is missing a trace_id or span_id it will
+return false, otherwise returns true. */
+bool validate_decode_context(google_trace_TraceContext *ctxt, uint8_t *buffer,
+                             size_t msg_length) {
+  // Validate the decoding of a context written to buffer.
+  if (!decode_trace_context(ctxt, buffer, msg_length)) {
+    return false;
+  }
+
+  if (!ctxt->has_trace_id || !ctxt->has_span_id) {
+    return false;
+  }
+
+  return true;
+}
+
+/* Read an encoded trace context from a file.  Validates that the decoding
+gives the expected result (succeed). */
+static void read_and_validate_context_from_file(google_trace_TraceContext *ctxt,
+                                                const char *file,
+                                                const bool succeed) {
+  uint8_t buffer[BUF_SIZE];
+  FILE *input = fopen(file, "rb");
+  GPR_ASSERT(input != NULL);
+  size_t nbytes = fread(buffer, 1, BUF_SIZE, input);
+  GPR_ASSERT(nbytes <= BUF_SIZE && feof(input) && !ferror(input));
+  bool res = validate_decode_context(ctxt, buffer, nbytes);
+  GPR_ASSERT(res == succeed);
+  GPR_ASSERT(fclose(input) == 0);
+}
+
+// Test full proto-buffer.
+static void test_full() {
+  google_trace_TraceContext ctxt = google_trace_TraceContext_init_zero;
+  read_and_validate_context_from_file(
+      &ctxt, "test/core/census/data/context_full.pb", true);
+}
+
+// Test empty proto-buffer.
+static void test_empty() {
+  google_trace_TraceContext ctxt = google_trace_TraceContext_init_zero;
+  read_and_validate_context_from_file(
+      &ctxt, "test/core/census/data/context_empty.pb", false);
+}
+
+// Test proto-buffer with only trace_id.
+static void test_trace_only() {
+  google_trace_TraceContext ctxt = google_trace_TraceContext_init_zero;
+  read_and_validate_context_from_file(
+      &ctxt, "test/core/census/data/context_trace_only.pb", false);
+}
+
+// Test proto-buffer with only span_id.
+static void test_span_only() {
+  google_trace_TraceContext ctxt = google_trace_TraceContext_init_zero;
+  read_and_validate_context_from_file(
+      &ctxt, "test/core/census/data/context_span_only.pb", false);
+}
+
+// Test proto-buffer without is_sampled value.
+static void test_no_sample() {
+  google_trace_TraceContext ctxt = google_trace_TraceContext_init_zero;
+  read_and_validate_context_from_file(
+      &ctxt, "test/core/census/data/context_no_sample.pb", true);
+  GPR_ASSERT(ctxt.has_is_sampled == false && ctxt.is_sampled == false);
+}
+
+static void test_encode_decode() {
+  uint8_t buffer[BUF_SIZE] = {0};
+
+  google_trace_TraceContext ctxt1 = google_trace_TraceContext_init_zero;
+  ctxt1.has_trace_id = true;
+  ctxt1.trace_id.has_hi = true;
+  ctxt1.trace_id.has_lo = true;
+  ctxt1.trace_id.lo = 1;
+  ctxt1.trace_id.hi = 2;
+  ctxt1.has_span_id = true;
+  ctxt1.span_id = 3;
+  validate_encode_decode_context(&ctxt1, buffer, sizeof(buffer));
+
+  google_trace_TraceContext ctxt2 = google_trace_TraceContext_init_zero;
+  ctxt2.has_trace_id = true;
+  ctxt2.trace_id.has_hi = false;
+  ctxt2.trace_id.has_lo = false;
+  ctxt2.has_span_id = true;
+  validate_encode_decode_context(&ctxt2, buffer, sizeof(buffer));
+}
+
+// Test a corrupted proto-buffer.
+static void test_corrupt() {
+  uint8_t buffer[BUF_SIZE] = {0};
+  google_trace_TraceContext ctxt1 = google_trace_TraceContext_init_zero;
+  size_t msg_length;
+
+  ctxt1.has_trace_id = true;
+  ctxt1.trace_id.has_hi = true;
+  ctxt1.trace_id.has_lo = true;
+  ctxt1.trace_id.lo = 1;
+  ctxt1.trace_id.hi = 2;
+  ctxt1.has_span_id = true;
+  ctxt1.span_id = 3;
+  ctxt1.is_sampled = true;
+  msg_length = encode_trace_context(&ctxt1, buffer, sizeof(buffer));
+
+  /* Corrupt some bytes.  255 (0xFF) should be illegal for the first byte of the
+  proto encoded object. */
+  buffer[0] = 255;
+
+  bool res = validate_decode_context(&ctxt1, buffer, msg_length);
+  GPR_ASSERT(res == false);
+}
+
+static void test_buffer_size() {
+  // This buffer is too small, so the encode should fail.
+  uint8_t buffer[16] = {0};
+  google_trace_TraceContext ctxt1 = google_trace_TraceContext_init_zero;
+  size_t msg_length;
+
+  ctxt1.has_trace_id = true;
+  ctxt1.trace_id.has_hi = true;
+  ctxt1.trace_id.has_lo = true;
+  ctxt1.trace_id.lo = 1;
+  ctxt1.trace_id.hi = 2;
+  ctxt1.has_span_id = true;
+  ctxt1.span_id = 3;
+  ctxt1.is_sampled = true;
+  msg_length = encode_trace_context(&ctxt1, buffer, sizeof(buffer));
+
+  GPR_ASSERT(msg_length == 0);
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  test_full();
+  test_empty();
+  test_trace_only();
+  test_span_only();
+  test_encode_decode();
+  test_corrupt();
+  test_no_sample();
+  test_buffer_size();
+
+  return 0;
+}
diff --git a/test/core/channel/channel_stack_test.c b/test/core/channel/channel_stack_test.c
index f9561be..b1c1ed9 100644
--- a/test/core/channel/channel_stack_test.c
+++ b/test/core/channel/channel_stack_test.c
@@ -53,17 +53,20 @@
   *(int *)(elem->channel_data) = 0;
 }
 
-static void call_init_func(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {
+static grpc_error *call_init_func(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  grpc_call_element_args *args) {
   ++*(int *)(elem->channel_data);
   *(int *)(elem->call_data) = 0;
+  return GRPC_ERROR_NONE;
 }
 
 static void channel_destroy_func(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {}
 
 static void call_destroy_func(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats, void *ignored) {
+                              const grpc_call_final_info *final_info,
+                              void *ignored) {
   ++*(int *)(elem->channel_data);
 }
 
@@ -132,8 +135,10 @@
   GPR_ASSERT(*channel_data == 0);
 
   call_stack = gpr_malloc(channel_stack->call_stack_size);
-  grpc_call_stack_init(&exec_ctx, channel_stack, 1, free_call, call_stack, NULL,
-                       NULL, call_stack);
+  grpc_error *error = grpc_call_stack_init(
+      &exec_ctx, channel_stack, 1, free_call, call_stack, NULL, NULL,
+      gpr_inf_future(GPR_CLOCK_MONOTONIC), call_stack);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   GPR_ASSERT(call_stack->count == 1);
   call_elem = grpc_call_stack_element(call_stack, 0);
   GPR_ASSERT(call_elem->filter == channel_elem->filter);
diff --git a/test/core/client_config/lb_policies_test.c b/test/core/client_config/lb_policies_test.c
index 3160312..fafff7b 100644
--- a/test/core/client_config/lb_policies_test.c
+++ b/test/core/client_config/lb_policies_test.c
@@ -48,6 +48,7 @@
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/server.h"
 #include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/fake_resolver.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
 
@@ -351,9 +352,9 @@
                                                        ops, (size_t)(op - ops),
                                                        tag(102), NULL));
 
-      cq_expect_completion(cqv, tag(102), 1);
+      CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
       if (!completed_client) {
-        cq_expect_completion(cqv, tag(1), 1);
+        CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
       }
       cq_verify(cqv);
 
@@ -376,7 +377,7 @@
     } else { /* no response from server */
       grpc_call_cancel(c, NULL);
       if (!completed_client) {
-        cq_expect_completion(cqv, tag(1), 1);
+        CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
         cq_verify(cqv);
       }
     }
@@ -508,7 +509,7 @@
   /* Create client. */
   servers_hostports_str = gpr_strjoin_sep((const char **)f->servers_hostports,
                                           f->num_servers, ",", NULL);
-  gpr_asprintf(&client_hostport, "ipv4:%s?lb_policy=round_robin",
+  gpr_asprintf(&client_hostport, "test:%s?lb_policy=round_robin",
                servers_hostports_str);
 
   arg.type = GRPC_ARG_INTEGER;
@@ -544,7 +545,7 @@
 
   servers_hostports_str = gpr_strjoin_sep((const char **)f->servers_hostports,
                                           f->num_servers, ",", NULL);
-  gpr_asprintf(&client_hostport, "ipv4:%s?lb_policy=round_robin",
+  gpr_asprintf(&client_hostport, "test:%s?lb_policy=round_robin",
                servers_hostports_str);
 
   arg.type = GRPC_ARG_INTEGER;
@@ -576,7 +577,7 @@
   client = create_client(f);
 
   grpc_channel_ping(client, f->cq, tag(0), NULL);
-  cq_expect_completion(cqv, tag(0), 0);
+  CQ_EXPECT_COMPLETION(cqv, tag(0), 0);
 
   /* check that we're still in idle, and start connecting */
   GPR_ASSERT(grpc_channel_check_connectivity_state(client, 1) ==
@@ -586,7 +587,7 @@
   while (state != GRPC_CHANNEL_READY) {
     grpc_channel_watch_connectivity_state(
         client, state, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3), f->cq, tag(99));
-    cq_expect_completion(cqv, tag(99), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(99), 1);
     cq_verify(cqv);
     state = grpc_channel_check_connectivity_state(client, 0);
     GPR_ASSERT(state == GRPC_CHANNEL_READY ||
@@ -596,7 +597,7 @@
 
   for (i = 1; i <= 5; i++) {
     grpc_channel_ping(client, f->cq, tag(i), NULL);
-    cq_expect_completion(cqv, tag(i), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(i), 1);
     cq_verify(cqv);
   }
   gpr_free(rdata.call_details);
@@ -874,6 +875,7 @@
   const size_t NUM_SERVERS = 4;
 
   grpc_test_init(argc, argv);
+  grpc_fake_resolver_init();
   grpc_init();
   grpc_tracer_set_enabled("round_robin", 1);
 
diff --git a/test/core/client_config/resolvers/dns_resolver_connectivity_test.c b/test/core/client_config/resolvers/dns_resolver_connectivity_test.c
index 69c07d8..d3b9619 100644
--- a/test/core/client_config/resolvers/dns_resolver_connectivity_test.c
+++ b/test/core/client_config/resolvers/dns_resolver_connectivity_test.c
@@ -41,29 +41,6 @@
 #include "src/core/lib/iomgr/timer.h"
 #include "test/core/util/test_config.h"
 
-static void client_channel_factory_ref(grpc_client_channel_factory *scv) {}
-static void client_channel_factory_unref(grpc_exec_ctx *exec_ctx,
-                                         grpc_client_channel_factory *scv) {}
-static grpc_subchannel *client_channel_factory_create_subchannel(
-    grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory,
-    grpc_subchannel_args *args) {
-  return NULL;
-}
-
-static grpc_channel *client_channel_factory_create_channel(
-    grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory,
-    const char *target, grpc_client_channel_type type,
-    grpc_channel_args *args) {
-  GPR_UNREACHABLE_CODE(return NULL);
-}
-
-static const grpc_client_channel_factory_vtable sc_vtable = {
-    client_channel_factory_ref, client_channel_factory_unref,
-    client_channel_factory_create_subchannel,
-    client_channel_factory_create_channel};
-
-static grpc_client_channel_factory cc_factory = {&sc_vtable};
-
 static gpr_mu g_mu;
 static bool g_fail_resolution = true;
 
@@ -92,7 +69,6 @@
   grpc_resolver_args args;
   memset(&args, 0, sizeof(args));
   args.uri = uri;
-  args.client_channel_factory = &cc_factory;
   grpc_resolver *resolver =
       grpc_resolver_factory_create_resolver(factory, &args);
   grpc_resolver_factory_unref(factory);
@@ -127,26 +103,26 @@
 
   grpc_resolver *resolver = create_resolver("dns:test");
 
-  grpc_client_config *config = (grpc_client_config *)1;
+  grpc_resolver_result *result = (grpc_resolver_result *)1;
 
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   gpr_event ev1;
   gpr_event_init(&ev1);
-  grpc_resolver_next(&exec_ctx, resolver, &config,
+  grpc_resolver_next(&exec_ctx, resolver, &result,
                      grpc_closure_create(on_done, &ev1));
   grpc_exec_ctx_flush(&exec_ctx);
   GPR_ASSERT(wait_loop(5, &ev1));
-  GPR_ASSERT(config == NULL);
+  GPR_ASSERT(result == NULL);
 
   gpr_event ev2;
   gpr_event_init(&ev2);
-  grpc_resolver_next(&exec_ctx, resolver, &config,
+  grpc_resolver_next(&exec_ctx, resolver, &result,
                      grpc_closure_create(on_done, &ev2));
   grpc_exec_ctx_flush(&exec_ctx);
   GPR_ASSERT(wait_loop(30, &ev2));
-  GPR_ASSERT(config != NULL);
+  GPR_ASSERT(result != NULL);
 
-  grpc_client_config_unref(&exec_ctx, config);
+  grpc_resolver_result_unref(&exec_ctx, result);
   GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "test");
   grpc_exec_ctx_finish(&exec_ctx);
 
diff --git a/test/core/client_config/resolvers/dns_resolver_test.c b/test/core/client_config/resolvers/dns_resolver_test.c
index 21dc99c..c3f4cb1 100644
--- a/test/core/client_config/resolvers/dns_resolver_test.c
+++ b/test/core/client_config/resolvers/dns_resolver_test.c
@@ -38,29 +38,6 @@
 #include "src/core/ext/client_config/resolver_registry.h"
 #include "test/core/util/test_config.h"
 
-static void client_channel_factory_ref(grpc_client_channel_factory *scv) {}
-static void client_channel_factory_unref(grpc_exec_ctx *exec_ctx,
-                                         grpc_client_channel_factory *scv) {}
-static grpc_subchannel *client_channel_factory_create_subchannel(
-    grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory,
-    grpc_subchannel_args *args) {
-  GPR_UNREACHABLE_CODE(return NULL);
-}
-
-static grpc_channel *client_channel_factory_create_channel(
-    grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory,
-    const char *target, grpc_client_channel_type type,
-    grpc_channel_args *args) {
-  GPR_UNREACHABLE_CODE(return NULL);
-}
-
-static const grpc_client_channel_factory_vtable sc_vtable = {
-    client_channel_factory_ref, client_channel_factory_unref,
-    client_channel_factory_create_subchannel,
-    client_channel_factory_create_channel};
-
-static grpc_client_channel_factory cc_factory = {&sc_vtable};
-
 static void test_succeeds(grpc_resolver_factory *factory, const char *string) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_uri *uri = grpc_uri_parse(string, 0);
@@ -71,7 +48,6 @@
   GPR_ASSERT(uri);
   memset(&args, 0, sizeof(args));
   args.uri = uri;
-  args.client_channel_factory = &cc_factory;
   resolver = grpc_resolver_factory_create_resolver(factory, &args);
   GPR_ASSERT(resolver != NULL);
   GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "test_succeeds");
diff --git a/test/core/client_config/resolvers/sockaddr_resolver_test.c b/test/core/client_config/resolvers/sockaddr_resolver_test.c
index b11546b..b5d96ef 100644
--- a/test/core/client_config/resolvers/sockaddr_resolver_test.c
+++ b/test/core/client_config/resolvers/sockaddr_resolver_test.c
@@ -33,34 +33,28 @@
 
 #include <string.h>
 
+#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 
 #include "src/core/ext/client_config/resolver_registry.h"
+#include "src/core/ext/client_config/resolver_result.h"
+
 #include "test/core/util/test_config.h"
 
-static void client_channel_factory_ref(grpc_client_channel_factory *scv) {}
-static void client_channel_factory_unref(grpc_exec_ctx *exec_ctx,
-                                         grpc_client_channel_factory *scv) {}
-static grpc_subchannel *client_channel_factory_create_subchannel(
-    grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory,
-    grpc_subchannel_args *args) {
-  GPR_UNREACHABLE_CODE(return NULL);
+typedef struct on_resolution_arg {
+  char *expected_server_name;
+  grpc_resolver_result *resolver_result;
+} on_resolution_arg;
+
+void on_resolution_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  on_resolution_arg *res = arg;
+  const char *server_name =
+      grpc_resolver_result_get_server_name(res->resolver_result);
+  GPR_ASSERT(strcmp(res->expected_server_name, server_name) == 0);
+  grpc_resolver_result_unref(exec_ctx, res->resolver_result);
 }
 
-static grpc_channel *client_channel_factory_create_channel(
-    grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory,
-    const char *target, grpc_client_channel_type type,
-    grpc_channel_args *args) {
-  GPR_UNREACHABLE_CODE(return NULL);
-}
-
-static const grpc_client_channel_factory_vtable sc_vtable = {
-    client_channel_factory_ref, client_channel_factory_unref,
-    client_channel_factory_create_subchannel,
-    client_channel_factory_create_channel};
-
-static grpc_client_channel_factory cc_factory = {&sc_vtable};
-
 static void test_succeeds(grpc_resolver_factory *factory, const char *string) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_uri *uri = grpc_uri_parse(string, 0);
@@ -71,12 +65,20 @@
   GPR_ASSERT(uri);
   memset(&args, 0, sizeof(args));
   args.uri = uri;
-  args.client_channel_factory = &cc_factory;
   resolver = grpc_resolver_factory_create_resolver(factory, &args);
   GPR_ASSERT(resolver != NULL);
+
+  on_resolution_arg on_res_arg;
+  memset(&on_res_arg, 0, sizeof(on_res_arg));
+  on_res_arg.expected_server_name = uri->path;
+  grpc_closure *on_resolution =
+      grpc_closure_create(on_resolution_cb, &on_res_arg);
+
+  grpc_resolver_next(&exec_ctx, resolver, &on_res_arg.resolver_result,
+                     on_resolution);
   GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "test_succeeds");
-  grpc_uri_destroy(uri);
   grpc_exec_ctx_finish(&exec_ctx);
+  grpc_uri_destroy(uri);
 }
 
 static void test_fails(grpc_resolver_factory *factory, const char *string) {
diff --git a/test/core/end2end/bad_server_response_test.c b/test/core/end2end/bad_server_response_test.c
index ab80adf..5ed0eb6 100644
--- a/test/core/end2end/bad_server_response_test.c
+++ b/test/core/end2end/bad_server_response_test.c
@@ -206,7 +206,7 @@
 
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   gpr_log(GPR_DEBUG, "Rpc status: %d, details: %s", status, details);
diff --git a/test/core/end2end/connection_refused_test.c b/test/core/end2end/connection_refused_test.c
new file mode 100644
index 0000000..4149159
--- /dev/null
+++ b/test/core/end2end/connection_refused_test.c
@@ -0,0 +1,128 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <string.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"
+
+static void *tag(intptr_t i) { return (void *)i; }
+
+static void run_test(bool fail_fast) {
+  grpc_channel *chan;
+  grpc_call *call;
+  gpr_timespec deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(2);
+  grpc_completion_queue *cq;
+  cq_verifier *cqv;
+  grpc_op ops[6];
+  grpc_op *op;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_status_code status;
+  char *details = NULL;
+  size_t details_capacity = 0;
+
+  gpr_log(GPR_INFO, "TEST: fail_fast=%d", fail_fast);
+
+  grpc_init();
+
+  grpc_metadata_array_init(&trailing_metadata_recv);
+
+  cq = grpc_completion_queue_create(NULL);
+  cqv = cq_verifier_create(cq);
+
+  /* create a call, channel to a port which will refuse connection */
+  int port = grpc_pick_unused_port_or_die();
+  char *addr;
+  gpr_join_host_port(&addr, "localhost", port);
+
+  chan = grpc_insecure_channel_create(addr, NULL, NULL);
+  call = grpc_channel_create_call(chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
+                                  "/Foo", "nonexistant", deadline, NULL);
+
+  gpr_free(addr);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = fail_fast ? 0 : GRPC_INITIAL_METADATA_IGNORE_CONNECTIVITY;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(
+                                 call, ops, (size_t)(op - ops), tag(1), NULL));
+  /* verify that all tags get completed */
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+  cq_verify(cqv);
+
+  if (fail_fast) {
+    GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE);
+  } else {
+    GPR_ASSERT(status == GRPC_STATUS_DEADLINE_EXCEEDED);
+  }
+
+  grpc_completion_queue_shutdown(cq);
+  while (
+      grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL)
+          .type != GRPC_QUEUE_SHUTDOWN)
+    ;
+  grpc_completion_queue_destroy(cq);
+  grpc_call_destroy(call);
+  grpc_channel_destroy(chan);
+  cq_verifier_destroy(cqv);
+
+  gpr_free(details);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+
+  grpc_shutdown();
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  run_test(true);
+  run_test(false);
+  return 0;
+}
diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c
index 890309c..1f42d34 100644
--- a/test/core/end2end/cq_verifier.c
+++ b/test/core/end2end/cq_verifier.c
@@ -61,7 +61,8 @@
    list to detail other expectations */
 typedef struct expectation {
   struct expectation *next;
-  struct expectation *prev;
+  const char *file;
+  int line;
   grpc_completion_type type;
   void *tag;
   int success;
@@ -71,17 +72,14 @@
 struct cq_verifier {
   /* bound completion queue */
   grpc_completion_queue *cq;
-  /* the root/sentinal expectation */
-  expectation expect;
+  /* start of expectation list */
+  expectation *first_expectation;
 };
 
 cq_verifier *cq_verifier_create(grpc_completion_queue *cq) {
   cq_verifier *v = gpr_malloc(sizeof(cq_verifier));
-  v->expect.type = ROOT_EXPECTATION;
-  v->expect.tag = NULL;
-  v->expect.next = &v->expect;
-  v->expect.prev = &v->expect;
   v->cq = cq;
+  v->first_expectation = NULL;
   return v;
 }
 
@@ -184,7 +182,8 @@
 
   switch (e->type) {
     case GRPC_OP_COMPLETE:
-      gpr_asprintf(&tmp, "GRPC_OP_COMPLETE result=%d", e->success);
+      gpr_asprintf(&tmp, "GRPC_OP_COMPLETE result=%d %s:%d", e->success,
+                   e->file, e->line);
       gpr_strvec_add(buf, tmp);
       break;
     case GRPC_QUEUE_TIMEOUT:
@@ -198,7 +197,7 @@
 static void expectations_to_strvec(gpr_strvec *buf, cq_verifier *v) {
   expectation *e;
 
-  for (e = v->expect.next; e != &v->expect; e = e->next) {
+  for (e = v->first_expectation; e != NULL; e = e->next) {
     expectation_to_strvec(buf, e);
     gpr_strvec_add(buf, gpr_strdup("\n"));
   }
@@ -218,53 +217,48 @@
 }
 
 void cq_verify(cq_verifier *v) {
-  gpr_timespec deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10);
-  grpc_event ev;
-  expectation *e;
-  char *s;
-  gpr_strvec have_tags;
-
-  gpr_strvec_init(&have_tags);
-
-  while (v->expect.next != &v->expect) {
-    ev = grpc_completion_queue_next(v->cq, deadline, NULL);
+  const gpr_timespec deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10);
+  while (v->first_expectation != NULL) {
+    grpc_event ev = grpc_completion_queue_next(v->cq, deadline, NULL);
     if (ev.type == GRPC_QUEUE_TIMEOUT) {
       fail_no_event_received(v);
       break;
     }
-
-    for (e = v->expect.next; e != &v->expect; e = e->next) {
-      gpr_asprintf(&s, " %p", e->tag);
-      gpr_strvec_add(&have_tags, s);
+    expectation *e;
+    expectation *prev = NULL;
+    for (e = v->first_expectation; e != NULL; e = e->next) {
       if (e->tag == ev.tag) {
         verify_matches(e, &ev);
-        e->next->prev = e->prev;
-        e->prev->next = e->next;
+        if (e == v->first_expectation) v->first_expectation = e->next;
+        if (prev != NULL) prev->next = e->next;
         gpr_free(e);
         break;
       }
+      prev = e;
     }
-    if (e == &v->expect) {
-      s = grpc_event_string(&ev);
-      gpr_log(GPR_ERROR, "event not found: %s", s);
+    if (e == NULL) {
+      char *s = grpc_event_string(&ev);
+      gpr_log(GPR_ERROR, "cq returned unexpected event: %s", s);
       gpr_free(s);
-      s = gpr_strvec_flatten(&have_tags, NULL);
-      gpr_log(GPR_ERROR, "have tags:%s", s);
+      gpr_strvec expectations;
+      gpr_strvec_init(&expectations);
+      expectations_to_strvec(&expectations, v);
+      s = gpr_strvec_flatten(&expectations, NULL);
+      gpr_strvec_destroy(&expectations);
+      gpr_log(GPR_ERROR, "expected tags:\n%s", s);
       gpr_free(s);
-      gpr_strvec_destroy(&have_tags);
       abort();
     }
   }
-
-  gpr_strvec_destroy(&have_tags);
 }
 
-void cq_verify_empty(cq_verifier *v) {
-  gpr_timespec deadline = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
-                                       gpr_time_from_seconds(1, GPR_TIMESPAN));
+void cq_verify_empty_timeout(cq_verifier *v, int timeout_sec) {
+  gpr_timespec deadline =
+      gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+                   gpr_time_from_seconds(timeout_sec, GPR_TIMESPAN));
   grpc_event ev;
 
-  GPR_ASSERT(v->expect.next == &v->expect && "expectation queue must be empty");
+  GPR_ASSERT(v->first_expectation == NULL && "expectation queue must be empty");
 
   ev = grpc_completion_queue_next(v->cq, deadline, NULL);
   if (ev.type != GRPC_QUEUE_TIMEOUT) {
@@ -275,16 +269,21 @@
   }
 }
 
-static expectation *add(cq_verifier *v, grpc_completion_type type, void *tag) {
+void cq_verify_empty(cq_verifier *v) { cq_verify_empty_timeout(v, 1); }
+
+static void add(cq_verifier *v, const char *file, int line,
+                grpc_completion_type type, void *tag, bool success) {
   expectation *e = gpr_malloc(sizeof(expectation));
   e->type = type;
+  e->file = file;
+  e->line = line;
   e->tag = tag;
-  e->next = &v->expect;
-  e->prev = e->next->prev;
-  e->next->prev = e->prev->next = e;
-  return e;
+  e->success = success;
+  e->next = v->first_expectation;
+  v->first_expectation = e;
 }
 
-void cq_expect_completion(cq_verifier *v, void *tag, bool success) {
-  add(v, GRPC_OP_COMPLETE, tag)->success = success;
+void cq_expect_completion(cq_verifier *v, const char *file, int line, void *tag,
+                          bool success) {
+  add(v, file, line, GRPC_OP_COMPLETE, tag, success);
 }
diff --git a/test/core/end2end/cq_verifier.h b/test/core/end2end/cq_verifier.h
index 8c9a85c..0a7c03c 100644
--- a/test/core/end2end/cq_verifier.h
+++ b/test/core/end2end/cq_verifier.h
@@ -55,11 +55,17 @@
 /* ensure that the completion queue is empty */
 void cq_verify_empty(cq_verifier *v);
 
+/* ensure that the completion queue is empty, waiting up to \a timeout secs. */
+void cq_verify_empty_timeout(cq_verifier *v, int timeout_sec);
+
 /* Various expectation matchers
    Any functions taking ... expect a NULL terminated list of key/value pairs
    (each pair using two parameter slots) of metadata that MUST be present in
    the event. */
-void cq_expect_completion(cq_verifier *v, void *tag, bool success);
+void cq_expect_completion(cq_verifier *v, const char *file, int line, void *tag,
+                          bool success);
+#define CQ_EXPECT_COMPLETION(v, tag, success) \
+  cq_expect_completion(v, __FILE__, __LINE__, tag, success)
 
 int byte_buffer_eq_string(grpc_byte_buffer *byte_buffer, const char *string);
 int contains_metadata(grpc_metadata_array *array, const char *key,
diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c
index 348b9ed..8abb81c 100644
--- a/test/core/end2end/dualstack_socket_test.c
+++ b/test/core/end2end/dualstack_socket_test.c
@@ -199,7 +199,7 @@
     error = grpc_server_request_call(server, &s, &call_details,
                                      &request_metadata_recv, cq, cq, tag(101));
     GPR_ASSERT(GRPC_CALL_OK == error);
-    cq_expect_completion(cqv, tag(101), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
     cq_verify(cqv);
 
     memset(ops, 0, sizeof(ops));
@@ -221,8 +221,8 @@
     error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
     GPR_ASSERT(GRPC_CALL_OK == error);
 
-    cq_expect_completion(cqv, tag(102), 1);
-    cq_expect_completion(cqv, tag(1), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
     cq_verify(cqv);
 
     peer = grpc_call_get_peer(c);
@@ -238,7 +238,7 @@
     grpc_call_destroy(s);
   } else {
     /* Check for a failed connection. */
-    cq_expect_completion(cqv, tag(1), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
     cq_verify(cqv);
 
     GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE);
diff --git a/test/core/end2end/end2end_nosec_tests.c b/test/core/end2end/end2end_nosec_tests.c
index 03e55f1..a630262 100644
--- a/test/core/end2end/end2end_nosec_tests.c
+++ b/test/core/end2end/end2end_nosec_tests.c
@@ -69,6 +69,8 @@
 extern void disappearing_server_pre_init(void);
 extern void empty_batch(grpc_end2end_test_config config);
 extern void empty_batch_pre_init(void);
+extern void filter_call_init_fails(grpc_end2end_test_config config);
+extern void filter_call_init_fails_pre_init(void);
 extern void filter_causes_close(grpc_end2end_test_config config);
 extern void filter_causes_close_pre_init(void);
 extern void graceful_server_shutdown(grpc_end2end_test_config config);
@@ -83,6 +85,8 @@
 extern void invoke_large_request_pre_init(void);
 extern void large_metadata(grpc_end2end_test_config config);
 extern void large_metadata_pre_init(void);
+extern void load_reporting_hook(grpc_end2end_test_config config);
+extern void load_reporting_hook_pre_init(void);
 extern void max_concurrent_streams(grpc_end2end_test_config config);
 extern void max_concurrent_streams_pre_init(void);
 extern void max_message_length(grpc_end2end_test_config config);
@@ -91,6 +95,8 @@
 extern void negative_deadline_pre_init(void);
 extern void network_status_change(grpc_end2end_test_config config);
 extern void network_status_change_pre_init(void);
+extern void no_logging(grpc_end2end_test_config config);
+extern void no_logging_pre_init(void);
 extern void no_op(grpc_end2end_test_config config);
 extern void no_op_pre_init(void);
 extern void payload(grpc_end2end_test_config config);
@@ -111,6 +117,8 @@
 extern void shutdown_finishes_calls_pre_init(void);
 extern void shutdown_finishes_tags(grpc_end2end_test_config config);
 extern void shutdown_finishes_tags_pre_init(void);
+extern void simple_cacheable_request(grpc_end2end_test_config config);
+extern void simple_cacheable_request_pre_init(void);
 extern void simple_delayed_request(grpc_end2end_test_config config);
 extern void simple_delayed_request_pre_init(void);
 extern void simple_metadata(grpc_end2end_test_config config);
@@ -138,6 +146,7 @@
   default_host_pre_init();
   disappearing_server_pre_init();
   empty_batch_pre_init();
+  filter_call_init_fails_pre_init();
   filter_causes_close_pre_init();
   graceful_server_shutdown_pre_init();
   high_initial_seqno_pre_init();
@@ -145,10 +154,12 @@
   idempotent_request_pre_init();
   invoke_large_request_pre_init();
   large_metadata_pre_init();
+  load_reporting_hook_pre_init();
   max_concurrent_streams_pre_init();
   max_message_length_pre_init();
   negative_deadline_pre_init();
   network_status_change_pre_init();
+  no_logging_pre_init();
   no_op_pre_init();
   payload_pre_init();
   ping_pre_init();
@@ -159,6 +170,7 @@
   server_finishes_request_pre_init();
   shutdown_finishes_calls_pre_init();
   shutdown_finishes_tags_pre_init();
+  simple_cacheable_request_pre_init();
   simple_delayed_request_pre_init();
   simple_metadata_pre_init();
   simple_request_pre_init();
@@ -186,6 +198,7 @@
     default_host(config);
     disappearing_server(config);
     empty_batch(config);
+    filter_call_init_fails(config);
     filter_causes_close(config);
     graceful_server_shutdown(config);
     high_initial_seqno(config);
@@ -193,10 +206,12 @@
     idempotent_request(config);
     invoke_large_request(config);
     large_metadata(config);
+    load_reporting_hook(config);
     max_concurrent_streams(config);
     max_message_length(config);
     negative_deadline(config);
     network_status_change(config);
+    no_logging(config);
     no_op(config);
     payload(config);
     ping(config);
@@ -207,6 +222,7 @@
     server_finishes_request(config);
     shutdown_finishes_calls(config);
     shutdown_finishes_tags(config);
+    simple_cacheable_request(config);
     simple_delayed_request(config);
     simple_metadata(config);
     simple_request(config);
@@ -268,6 +284,10 @@
       empty_batch(config);
       continue;
     }
+    if (0 == strcmp("filter_call_init_fails", argv[i])) {
+      filter_call_init_fails(config);
+      continue;
+    }
     if (0 == strcmp("filter_causes_close", argv[i])) {
       filter_causes_close(config);
       continue;
@@ -296,6 +316,10 @@
       large_metadata(config);
       continue;
     }
+    if (0 == strcmp("load_reporting_hook", argv[i])) {
+      load_reporting_hook(config);
+      continue;
+    }
     if (0 == strcmp("max_concurrent_streams", argv[i])) {
       max_concurrent_streams(config);
       continue;
@@ -312,6 +336,10 @@
       network_status_change(config);
       continue;
     }
+    if (0 == strcmp("no_logging", argv[i])) {
+      no_logging(config);
+      continue;
+    }
     if (0 == strcmp("no_op", argv[i])) {
       no_op(config);
       continue;
@@ -352,6 +380,10 @@
       shutdown_finishes_tags(config);
       continue;
     }
+    if (0 == strcmp("simple_cacheable_request", argv[i])) {
+      simple_cacheable_request(config);
+      continue;
+    }
     if (0 == strcmp("simple_delayed_request", argv[i])) {
       simple_delayed_request(config);
       continue;
diff --git a/test/core/end2end/end2end_tests.c b/test/core/end2end/end2end_tests.c
index 877b1b1..925872a 100644
--- a/test/core/end2end/end2end_tests.c
+++ b/test/core/end2end/end2end_tests.c
@@ -71,6 +71,8 @@
 extern void disappearing_server_pre_init(void);
 extern void empty_batch(grpc_end2end_test_config config);
 extern void empty_batch_pre_init(void);
+extern void filter_call_init_fails(grpc_end2end_test_config config);
+extern void filter_call_init_fails_pre_init(void);
 extern void filter_causes_close(grpc_end2end_test_config config);
 extern void filter_causes_close_pre_init(void);
 extern void graceful_server_shutdown(grpc_end2end_test_config config);
@@ -85,6 +87,8 @@
 extern void invoke_large_request_pre_init(void);
 extern void large_metadata(grpc_end2end_test_config config);
 extern void large_metadata_pre_init(void);
+extern void load_reporting_hook(grpc_end2end_test_config config);
+extern void load_reporting_hook_pre_init(void);
 extern void max_concurrent_streams(grpc_end2end_test_config config);
 extern void max_concurrent_streams_pre_init(void);
 extern void max_message_length(grpc_end2end_test_config config);
@@ -93,6 +97,8 @@
 extern void negative_deadline_pre_init(void);
 extern void network_status_change(grpc_end2end_test_config config);
 extern void network_status_change_pre_init(void);
+extern void no_logging(grpc_end2end_test_config config);
+extern void no_logging_pre_init(void);
 extern void no_op(grpc_end2end_test_config config);
 extern void no_op_pre_init(void);
 extern void payload(grpc_end2end_test_config config);
@@ -113,6 +119,8 @@
 extern void shutdown_finishes_calls_pre_init(void);
 extern void shutdown_finishes_tags(grpc_end2end_test_config config);
 extern void shutdown_finishes_tags_pre_init(void);
+extern void simple_cacheable_request(grpc_end2end_test_config config);
+extern void simple_cacheable_request_pre_init(void);
 extern void simple_delayed_request(grpc_end2end_test_config config);
 extern void simple_delayed_request_pre_init(void);
 extern void simple_metadata(grpc_end2end_test_config config);
@@ -141,6 +149,7 @@
   default_host_pre_init();
   disappearing_server_pre_init();
   empty_batch_pre_init();
+  filter_call_init_fails_pre_init();
   filter_causes_close_pre_init();
   graceful_server_shutdown_pre_init();
   high_initial_seqno_pre_init();
@@ -148,10 +157,12 @@
   idempotent_request_pre_init();
   invoke_large_request_pre_init();
   large_metadata_pre_init();
+  load_reporting_hook_pre_init();
   max_concurrent_streams_pre_init();
   max_message_length_pre_init();
   negative_deadline_pre_init();
   network_status_change_pre_init();
+  no_logging_pre_init();
   no_op_pre_init();
   payload_pre_init();
   ping_pre_init();
@@ -162,6 +173,7 @@
   server_finishes_request_pre_init();
   shutdown_finishes_calls_pre_init();
   shutdown_finishes_tags_pre_init();
+  simple_cacheable_request_pre_init();
   simple_delayed_request_pre_init();
   simple_metadata_pre_init();
   simple_request_pre_init();
@@ -190,6 +202,7 @@
     default_host(config);
     disappearing_server(config);
     empty_batch(config);
+    filter_call_init_fails(config);
     filter_causes_close(config);
     graceful_server_shutdown(config);
     high_initial_seqno(config);
@@ -197,10 +210,12 @@
     idempotent_request(config);
     invoke_large_request(config);
     large_metadata(config);
+    load_reporting_hook(config);
     max_concurrent_streams(config);
     max_message_length(config);
     negative_deadline(config);
     network_status_change(config);
+    no_logging(config);
     no_op(config);
     payload(config);
     ping(config);
@@ -211,6 +226,7 @@
     server_finishes_request(config);
     shutdown_finishes_calls(config);
     shutdown_finishes_tags(config);
+    simple_cacheable_request(config);
     simple_delayed_request(config);
     simple_metadata(config);
     simple_request(config);
@@ -276,6 +292,10 @@
       empty_batch(config);
       continue;
     }
+    if (0 == strcmp("filter_call_init_fails", argv[i])) {
+      filter_call_init_fails(config);
+      continue;
+    }
     if (0 == strcmp("filter_causes_close", argv[i])) {
       filter_causes_close(config);
       continue;
@@ -304,6 +324,10 @@
       large_metadata(config);
       continue;
     }
+    if (0 == strcmp("load_reporting_hook", argv[i])) {
+      load_reporting_hook(config);
+      continue;
+    }
     if (0 == strcmp("max_concurrent_streams", argv[i])) {
       max_concurrent_streams(config);
       continue;
@@ -320,6 +344,10 @@
       network_status_change(config);
       continue;
     }
+    if (0 == strcmp("no_logging", argv[i])) {
+      no_logging(config);
+      continue;
+    }
     if (0 == strcmp("no_op", argv[i])) {
       no_op(config);
       continue;
@@ -360,6 +388,10 @@
       shutdown_finishes_tags(config);
       continue;
     }
+    if (0 == strcmp("simple_cacheable_request", argv[i])) {
+      simple_cacheable_request(config);
+      continue;
+    }
     if (0 == strcmp("simple_delayed_request", argv[i])) {
       simple_delayed_request(config);
       continue;
diff --git a/test/core/end2end/end2end_tests.h b/test/core/end2end/end2end_tests.h
index dfa041c..34af093 100644
--- a/test/core/end2end/end2end_tests.h
+++ b/test/core/end2end/end2end_tests.h
@@ -42,6 +42,7 @@
 #define FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION 1
 #define FEATURE_MASK_SUPPORTS_HOSTNAME_VERIFICATION 2
 #define FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS 4
+#define FEATURE_MASK_SUPPORTS_REQUEST_PROXYING 8
 
 #define FAIL_AUTH_CHECK_SERVER_ARG_NAME "fail_auth_check"
 
diff --git a/test/core/end2end/fake_resolver.c b/test/core/end2end/fake_resolver.c
new file mode 100644
index 0000000..8a6624a
--- /dev/null
+++ b/test/core/end2end/fake_resolver.c
@@ -0,0 +1,212 @@
+//
+// Copyright 2016, 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.
+//
+
+// This is similar to the sockaddr resolver, except that it supports a
+// bunch of query args that are useful for dependency injection in tests.
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/client_config/parse_address.h"
+#include "src/core/ext/client_config/resolver_registry.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+#include "src/core/lib/support/string.h"
+
+//
+// fake_resolver
+//
+
+typedef struct {
+  // base class -- must be first
+  grpc_resolver base;
+
+  // passed-in parameters
+  char* target_name;  // the path component of the uri passed in
+  grpc_lb_addresses* addresses;
+  char* lb_policy_name;
+
+  // mutex guarding the rest of the state
+  gpr_mu mu;
+  // have we published?
+  bool published;
+  // pending next completion, or NULL
+  grpc_closure* next_completion;
+  // target result address for next completion
+  grpc_resolver_result** target_result;
+} fake_resolver;
+
+static void fake_resolver_destroy(grpc_exec_ctx* exec_ctx, grpc_resolver* gr) {
+  fake_resolver* r = (fake_resolver*)gr;
+  gpr_mu_destroy(&r->mu);
+  gpr_free(r->target_name);
+  grpc_lb_addresses_destroy(r->addresses, NULL /* user_data_destroy */);
+  gpr_free(r->lb_policy_name);
+  gpr_free(r);
+}
+
+static void fake_resolver_shutdown(grpc_exec_ctx* exec_ctx,
+                                   grpc_resolver* resolver) {
+  fake_resolver* r = (fake_resolver*)resolver;
+  gpr_mu_lock(&r->mu);
+  if (r->next_completion != NULL) {
+    *r->target_result = NULL;
+    grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
+    r->next_completion = NULL;
+  }
+  gpr_mu_unlock(&r->mu);
+}
+
+static void fake_resolver_maybe_finish_next_locked(grpc_exec_ctx* exec_ctx,
+                                                   fake_resolver* r) {
+  if (r->next_completion != NULL && !r->published) {
+    r->published = true;
+    *r->target_result = grpc_resolver_result_create(
+        r->target_name,
+        grpc_lb_addresses_copy(r->addresses, NULL /* user_data_copy */),
+        r->lb_policy_name, NULL /* lb_policy_args */);
+    grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
+    r->next_completion = NULL;
+  }
+}
+
+static void fake_resolver_channel_saw_error(grpc_exec_ctx* exec_ctx,
+                                            grpc_resolver* resolver) {
+  fake_resolver* r = (fake_resolver*)resolver;
+  gpr_mu_lock(&r->mu);
+  r->published = false;
+  fake_resolver_maybe_finish_next_locked(exec_ctx, r);
+  gpr_mu_unlock(&r->mu);
+}
+
+static void fake_resolver_next(grpc_exec_ctx* exec_ctx, grpc_resolver* resolver,
+                               grpc_resolver_result** target_result,
+                               grpc_closure* on_complete) {
+  fake_resolver* r = (fake_resolver*)resolver;
+  gpr_mu_lock(&r->mu);
+  GPR_ASSERT(!r->next_completion);
+  r->next_completion = on_complete;
+  r->target_result = target_result;
+  fake_resolver_maybe_finish_next_locked(exec_ctx, r);
+  gpr_mu_unlock(&r->mu);
+}
+
+static const grpc_resolver_vtable fake_resolver_vtable = {
+    fake_resolver_destroy, fake_resolver_shutdown,
+    fake_resolver_channel_saw_error, fake_resolver_next};
+
+//
+// fake_resolver_factory
+//
+
+static void fake_resolver_factory_ref(grpc_resolver_factory* factory) {}
+
+static void fake_resolver_factory_unref(grpc_resolver_factory* factory) {}
+
+static void do_nothing(void* ignored) {}
+
+static grpc_resolver* fake_resolver_create(grpc_resolver_factory* factory,
+                                           grpc_resolver_args* args) {
+  if (0 != strcmp(args->uri->authority, "")) {
+    gpr_log(GPR_ERROR, "authority based uri's not supported by the %s scheme",
+            args->uri->scheme);
+    return NULL;
+  }
+  // Get lb_enabled arg.  Anything other than "0" is interpreted as true.
+  const char* lb_enabled_qpart =
+      grpc_uri_get_query_arg(args->uri, "lb_enabled");
+  const bool lb_enabled =
+      lb_enabled_qpart != NULL && strcmp("0", lb_enabled_qpart) != 0;
+  // Construct addresses.
+  gpr_slice path_slice =
+      gpr_slice_new(args->uri->path, strlen(args->uri->path), do_nothing);
+  gpr_slice_buffer path_parts;
+  gpr_slice_buffer_init(&path_parts);
+  gpr_slice_split(path_slice, ",", &path_parts);
+  grpc_lb_addresses* addresses = grpc_lb_addresses_create(path_parts.count);
+  bool errors_found = false;
+  for (size_t i = 0; i < addresses->num_addresses; i++) {
+    grpc_uri ith_uri = *args->uri;
+    char* part_str = gpr_dump_slice(path_parts.slices[i], GPR_DUMP_ASCII);
+    ith_uri.path = part_str;
+    if (!parse_ipv4(
+            &ith_uri,
+            (struct sockaddr_storage*)(&addresses->addresses[i].address.addr),
+            &addresses->addresses[i].address.len)) {
+      errors_found = true;
+    }
+    gpr_free(part_str);
+    addresses->addresses[i].is_balancer = lb_enabled;
+    if (errors_found) break;
+  }
+  gpr_slice_buffer_destroy(&path_parts);
+  gpr_slice_unref(path_slice);
+  if (errors_found) {
+    grpc_lb_addresses_destroy(addresses, NULL /* user_data_destroy */);
+    return NULL;
+  }
+  // Instantiate resolver.
+  fake_resolver* r = gpr_malloc(sizeof(fake_resolver));
+  memset(r, 0, sizeof(*r));
+  r->target_name = gpr_strdup(args->uri->path);
+  r->addresses = addresses;
+  r->lb_policy_name =
+      gpr_strdup(grpc_uri_get_query_arg(args->uri, "lb_policy"));
+  gpr_mu_init(&r->mu);
+  grpc_resolver_init(&r->base, &fake_resolver_vtable);
+  return &r->base;
+}
+
+static char* fake_resolver_get_default_authority(grpc_resolver_factory* factory,
+                                                 grpc_uri* uri) {
+  const char* path = uri->path;
+  if (path[0] == '/') ++path;
+  return gpr_strdup(path);
+}
+
+static const grpc_resolver_factory_vtable fake_resolver_factory_vtable = {
+    fake_resolver_factory_ref, fake_resolver_factory_unref,
+    fake_resolver_create, fake_resolver_get_default_authority, "test"};
+
+static grpc_resolver_factory fake_resolver_factory = {
+    &fake_resolver_factory_vtable};
+
+void grpc_fake_resolver_init(void) {
+  grpc_register_resolver_type(&fake_resolver_factory);
+}
diff --git a/test/core/end2end/fake_resolver.h b/test/core/end2end/fake_resolver.h
new file mode 100644
index 0000000..7a30347
--- /dev/null
+++ b/test/core/end2end/fake_resolver.h
@@ -0,0 +1,39 @@
+//
+// Copyright 2016, 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_TEST_CORE_END2END_FAKE_RESOLVER_H
+#define GRPC_TEST_CORE_END2END_FAKE_RESOLVER_H
+
+#include "test/core/util/test_config.h"
+
+void grpc_fake_resolver_init();
+
+#endif /* GRPC_TEST_CORE_END2END_FAKE_RESOLVER_H */
diff --git a/test/core/end2end/fixtures/h2_fd.c b/test/core/end2end/fixtures/h2_fd.c
index 89fa025..8561fee 100644
--- a/test/core/end2end/fixtures/h2_fd.c
+++ b/test/core/end2end/fixtures/h2_fd.c
@@ -95,7 +95,7 @@
   grpc_server_register_completion_queue(f->server, f->cq, NULL);
   grpc_server_start(f->server);
 
-  grpc_server_add_insecure_channel_from_fd(f->server, f->cq, sfd->fd_pair[1]);
+  grpc_server_add_insecure_channel_from_fd(f->server, NULL, sfd->fd_pair[1]);
 
   grpc_exec_ctx_finish(&exec_ctx);
 }
diff --git a/test/core/end2end/fixtures/h2_http_proxy.c b/test/core/end2end/fixtures/h2_http_proxy.c
new file mode 100644
index 0000000..a675a11
--- /dev/null
+++ b/test/core/end2end/fixtures/h2_http_proxy.c
@@ -0,0 +1,129 @@
+/*
+ *
+ * Copyright 2016, 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 <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/useful.h>
+#include "src/core/ext/client_config/client_channel.h"
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/channel/http_server_filter.h"
+#include "src/core/lib/support/env.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/server.h"
+#include "test/core/end2end/fixtures/http_proxy.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+typedef struct fullstack_fixture_data {
+  char *server_addr;
+  grpc_end2end_http_proxy *proxy;
+} fullstack_fixture_data;
+
+static grpc_end2end_test_fixture chttp2_create_fixture_fullstack(
+    grpc_channel_args *client_args, grpc_channel_args *server_args) {
+  grpc_end2end_test_fixture f;
+  memset(&f, 0, sizeof(f));
+
+  fullstack_fixture_data *ffd = gpr_malloc(sizeof(fullstack_fixture_data));
+  const int server_port = grpc_pick_unused_port_or_die();
+  gpr_join_host_port(&ffd->server_addr, "localhost", server_port);
+  ffd->proxy = grpc_end2end_http_proxy_create();
+
+  f.fixture_data = ffd;
+  f.cq = grpc_completion_queue_create(NULL);
+
+  return f;
+}
+
+void chttp2_init_client_fullstack(grpc_end2end_test_fixture *f,
+                                  grpc_channel_args *client_args) {
+  fullstack_fixture_data *ffd = f->fixture_data;
+  char *proxy_uri;
+  gpr_asprintf(&proxy_uri, "http://%s",
+               grpc_end2end_http_proxy_get_proxy_name(ffd->proxy));
+  gpr_setenv("http_proxy", proxy_uri);
+  gpr_free(proxy_uri);
+  f->client = grpc_insecure_channel_create(ffd->server_addr, client_args, NULL);
+  GPR_ASSERT(f->client);
+}
+
+void chttp2_init_server_fullstack(grpc_end2end_test_fixture *f,
+                                  grpc_channel_args *server_args) {
+  fullstack_fixture_data *ffd = f->fixture_data;
+  if (f->server) {
+    grpc_server_destroy(f->server);
+  }
+  f->server = grpc_server_create(server_args, NULL);
+  grpc_server_register_completion_queue(f->server, f->cq, NULL);
+  GPR_ASSERT(grpc_server_add_insecure_http2_port(f->server, ffd->server_addr));
+  grpc_server_start(f->server);
+}
+
+void chttp2_tear_down_fullstack(grpc_end2end_test_fixture *f) {
+  fullstack_fixture_data *ffd = f->fixture_data;
+  gpr_free(ffd->server_addr);
+  grpc_end2end_http_proxy_destroy(ffd->proxy);
+  gpr_free(ffd);
+}
+
+/* All test configurations */
+static grpc_end2end_test_config configs[] = {
+    {"chttp2/fullstack", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION,
+     chttp2_create_fixture_fullstack, chttp2_init_client_fullstack,
+     chttp2_init_server_fullstack, chttp2_tear_down_fullstack},
+};
+
+int main(int argc, char **argv) {
+  size_t i;
+
+  grpc_test_init(argc, argv);
+  grpc_end2end_tests_pre_init();
+  grpc_init();
+
+  for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
+    grpc_end2end_tests(argc, argv, configs[i]);
+  }
+
+  grpc_shutdown();
+
+  return 0;
+}
diff --git a/test/core/end2end/fixtures/h2_load_reporting.c b/test/core/end2end/fixtures/h2_load_reporting.c
new file mode 100644
index 0000000..f6d3923
--- /dev/null
+++ b/test/core/end2end/fixtures/h2_load_reporting.c
@@ -0,0 +1,125 @@
+/*
+ *
+ * Copyright 2016, 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 <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/useful.h>
+#include "src/core/ext/client_config/client_channel.h"
+#include "src/core/ext/load_reporting/load_reporting.h"
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/channel/http_server_filter.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/server.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+typedef struct load_reporting_fixture_data {
+  char *localaddr;
+} load_reporting_fixture_data;
+
+static grpc_end2end_test_fixture chttp2_create_fixture_load_reporting(
+    grpc_channel_args *client_args, grpc_channel_args *server_args) {
+  grpc_end2end_test_fixture f;
+  int port = grpc_pick_unused_port_or_die();
+  load_reporting_fixture_data *ffd =
+      gpr_malloc(sizeof(load_reporting_fixture_data));
+  memset(&f, 0, sizeof(f));
+
+  gpr_join_host_port(&ffd->localaddr, "localhost", port);
+
+  f.fixture_data = ffd;
+  f.cq = grpc_completion_queue_create(NULL);
+
+  return f;
+}
+
+void chttp2_init_client_load_reporting(grpc_end2end_test_fixture *f,
+                                       grpc_channel_args *client_args) {
+  load_reporting_fixture_data *ffd = f->fixture_data;
+  f->client = grpc_insecure_channel_create(ffd->localaddr, client_args, NULL);
+  GPR_ASSERT(f->client);
+}
+
+void chttp2_init_server_load_reporting(grpc_end2end_test_fixture *f,
+                                       grpc_channel_args *server_args) {
+  load_reporting_fixture_data *ffd = f->fixture_data;
+  grpc_arg arg = grpc_load_reporting_enable_arg();
+  if (f->server) {
+    grpc_server_destroy(f->server);
+  }
+  server_args = grpc_channel_args_copy_and_add(server_args, &arg, 1);
+  f->server = grpc_server_create(server_args, NULL);
+  grpc_channel_args_destroy(server_args);
+  grpc_server_register_completion_queue(f->server, f->cq, NULL);
+  GPR_ASSERT(grpc_server_add_insecure_http2_port(f->server, ffd->localaddr));
+  grpc_server_start(f->server);
+}
+
+void chttp2_tear_down_load_reporting(grpc_end2end_test_fixture *f) {
+  load_reporting_fixture_data *ffd = f->fixture_data;
+  gpr_free(ffd->localaddr);
+  gpr_free(ffd);
+}
+
+/* All test configurations */
+static grpc_end2end_test_config configs[] = {
+    {"chttp2/fullstack+load_reporting",
+     FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION,
+     chttp2_create_fixture_load_reporting, chttp2_init_client_load_reporting,
+     chttp2_init_server_load_reporting, chttp2_tear_down_load_reporting},
+};
+
+int main(int argc, char **argv) {
+  size_t i;
+
+  grpc_test_init(argc, argv);
+  grpc_end2end_tests_pre_init();
+  grpc_init();
+
+  for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
+    grpc_end2end_tests(argc, argv, configs[i]);
+  }
+
+  grpc_shutdown();
+
+  return 0;
+}
diff --git a/test/core/end2end/fixtures/h2_loadreporting.c b/test/core/end2end/fixtures/h2_loadreporting.c
deleted file mode 100644
index 4ed02f9..0000000
--- a/test/core/end2end/fixtures/h2_loadreporting.c
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- *
- * Copyright 2016, 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 <string.h>
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/host_port.h>
-#include <grpc/support/log.h>
-#include <grpc/support/sync.h>
-#include <grpc/support/thd.h>
-#include <grpc/support/useful.h>
-#include "src/core/ext/client_config/client_channel.h"
-#include "src/core/ext/load_reporting/load_reporting.h"
-#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
-#include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/channel/connected_channel.h"
-#include "src/core/lib/channel/http_server_filter.h"
-#include "src/core/lib/surface/channel.h"
-#include "src/core/lib/surface/server.h"
-#include "test/core/util/port.h"
-#include "test/core/util/test_config.h"
-
-static grpc_load_reporting_config *g_client_lrc;
-static grpc_load_reporting_config *g_server_lrc;
-
-typedef struct fullstack_fixture_data {
-  char *localaddr;
-} fullstack_fixture_data;
-
-static grpc_end2end_test_fixture chttp2_create_fixture_fullstack(
-    grpc_channel_args *client_args, grpc_channel_args *server_args) {
-  grpc_end2end_test_fixture f;
-  int port = grpc_pick_unused_port_or_die();
-  fullstack_fixture_data *ffd = gpr_malloc(sizeof(fullstack_fixture_data));
-  memset(&f, 0, sizeof(f));
-
-  gpr_join_host_port(&ffd->localaddr, "localhost", port);
-
-  f.fixture_data = ffd;
-  f.cq = grpc_completion_queue_create(NULL);
-
-  return f;
-}
-
-typedef struct {
-  int64_t total_bytes;
-  bool fully_processed;
-  uint32_t initial_token;
-  uint32_t final_token;
-} aggregated_bw_stats;
-
-static void sample_fn(const grpc_load_reporting_call_data *call_data,
-                      void *user_data) {
-  GPR_ASSERT(user_data != NULL);
-  aggregated_bw_stats *custom_stats = (aggregated_bw_stats *)user_data;
-  if (call_data == NULL) {
-    /* initial invocation */
-    custom_stats->initial_token = 0xDEADBEEF;
-  } else {
-    /* final invocation */
-    custom_stats->total_bytes =
-        (int64_t)(call_data->stats->transport_stream_stats.outgoing.data_bytes +
-                  call_data->stats->transport_stream_stats.incoming.data_bytes);
-    custom_stats->final_token = 0xCAFED00D;
-    custom_stats->fully_processed = true;
-  }
-}
-
-void chttp2_init_client_fullstack(grpc_end2end_test_fixture *f,
-                                  grpc_channel_args *client_args) {
-  fullstack_fixture_data *ffd = f->fixture_data;
-  grpc_arg arg = grpc_load_reporting_config_create_arg(g_client_lrc);
-  client_args = grpc_channel_args_copy_and_add(client_args, &arg, 1);
-  f->client = grpc_insecure_channel_create(ffd->localaddr, client_args, NULL);
-  grpc_channel_args_destroy(client_args);
-  GPR_ASSERT(f->client);
-}
-
-void chttp2_init_server_fullstack(grpc_end2end_test_fixture *f,
-                                  grpc_channel_args *server_args) {
-  fullstack_fixture_data *ffd = f->fixture_data;
-  if (f->server) {
-    grpc_server_destroy(f->server);
-  }
-  grpc_arg arg = grpc_load_reporting_config_create_arg(g_server_lrc);
-  server_args = grpc_channel_args_copy_and_add(server_args, &arg, 1);
-  f->server = grpc_server_create(server_args, NULL);
-  grpc_channel_args_destroy(server_args);
-  grpc_server_register_completion_queue(f->server, f->cq, NULL);
-  GPR_ASSERT(grpc_server_add_insecure_http2_port(f->server, ffd->localaddr));
-  grpc_server_start(f->server);
-}
-
-void chttp2_tear_down_fullstack(grpc_end2end_test_fixture *f) {
-  fullstack_fixture_data *ffd = f->fixture_data;
-  gpr_free(ffd->localaddr);
-  gpr_free(ffd);
-}
-
-/* All test configurations */
-static grpc_end2end_test_config configs[] = {
-    {"chttp2/fullstack+loadreporting", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION,
-     chttp2_create_fixture_fullstack, chttp2_init_client_fullstack,
-     chttp2_init_server_fullstack, chttp2_tear_down_fullstack},
-};
-
-int main(int argc, char **argv) {
-  size_t i;
-
-  aggregated_bw_stats *aggr_stats_client =
-      gpr_malloc(sizeof(aggregated_bw_stats));
-  aggr_stats_client->total_bytes = -1;
-  aggr_stats_client->fully_processed = false;
-  aggregated_bw_stats *aggr_stats_server =
-      gpr_malloc(sizeof(aggregated_bw_stats));
-  aggr_stats_server->total_bytes = -1;
-  aggr_stats_server->fully_processed = false;
-
-  g_client_lrc =
-      grpc_load_reporting_config_create(sample_fn, aggr_stats_client);
-  g_server_lrc =
-      grpc_load_reporting_config_create(sample_fn, aggr_stats_server);
-
-  grpc_test_init(argc, argv);
-  grpc_end2end_tests_pre_init();
-  grpc_init();
-
-  for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
-    grpc_end2end_tests(argc, argv, configs[i]);
-  }
-
-  grpc_shutdown();
-
-  grpc_load_reporting_config_destroy(g_client_lrc);
-  grpc_load_reporting_config_destroy(g_server_lrc);
-
-  if (aggr_stats_client->fully_processed) {
-    GPR_ASSERT(aggr_stats_client->total_bytes >= 0);
-    GPR_ASSERT(aggr_stats_client->initial_token == 0xDEADBEEF);
-    GPR_ASSERT(aggr_stats_client->final_token == 0xCAFED00D);
-  }
-  if (aggr_stats_server->fully_processed) {
-    GPR_ASSERT(aggr_stats_server->total_bytes >= 0);
-    GPR_ASSERT(aggr_stats_server->initial_token == 0xDEADBEEF);
-    GPR_ASSERT(aggr_stats_server->final_token == 0xCAFED00D);
-  }
-
-  gpr_free(aggr_stats_client);
-  gpr_free(aggr_stats_server);
-
-  return 0;
-}
diff --git a/test/core/end2end/fixtures/h2_proxy.c b/test/core/end2end/fixtures/h2_proxy.c
index 8c50eeb..c7b9986 100644
--- a/test/core/end2end/fixtures/h2_proxy.c
+++ b/test/core/end2end/fixtures/h2_proxy.c
@@ -113,7 +113,8 @@
 
 /* All test configurations */
 static grpc_end2end_test_config configs[] = {
-    {"chttp2/fullstack+proxy", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION,
+    {"chttp2/fullstack+proxy", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
+                                   FEATURE_MASK_SUPPORTS_REQUEST_PROXYING,
      chttp2_create_fixture_fullstack, chttp2_init_client_fullstack,
      chttp2_init_server_fullstack, chttp2_tear_down_fullstack},
 };
diff --git a/test/core/end2end/fixtures/h2_sockpair+trace.c b/test/core/end2end/fixtures/h2_sockpair+trace.c
index 6b0769b..b8a5257 100644
--- a/test/core/end2end/fixtures/h2_sockpair+trace.c
+++ b/test/core/end2end/fixtures/h2_sockpair+trace.c
@@ -108,7 +108,7 @@
       grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client, 1);
   client_setup_transport(&exec_ctx, &cs, transport);
   GPR_ASSERT(f->client);
-  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
@@ -124,7 +124,7 @@
   transport =
       grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server, 0);
   server_setup_transport(f, transport);
-  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
diff --git a/test/core/end2end/fixtures/h2_sockpair.c b/test/core/end2end/fixtures/h2_sockpair.c
index 7be88f8..a57990d 100644
--- a/test/core/end2end/fixtures/h2_sockpair.c
+++ b/test/core/end2end/fixtures/h2_sockpair.c
@@ -107,7 +107,7 @@
       grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client, 1);
   client_setup_transport(&exec_ctx, &cs, transport);
   GPR_ASSERT(f->client);
-  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
@@ -123,7 +123,7 @@
   transport =
       grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server, 0);
   server_setup_transport(f, transport);
-  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
diff --git a/test/core/end2end/fixtures/h2_sockpair_1byte.c b/test/core/end2end/fixtures/h2_sockpair_1byte.c
index 166654b..50aac80 100644
--- a/test/core/end2end/fixtures/h2_sockpair_1byte.c
+++ b/test/core/end2end/fixtures/h2_sockpair_1byte.c
@@ -107,7 +107,7 @@
       grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client, 1);
   client_setup_transport(&exec_ctx, &cs, transport);
   GPR_ASSERT(f->client);
-  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
@@ -123,7 +123,7 @@
   transport =
       grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server, 0);
   server_setup_transport(f, transport);
-  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
diff --git a/test/core/end2end/fixtures/h2_ssl_cert.c b/test/core/end2end/fixtures/h2_ssl_cert.c
index b476bf5..ae2604d 100644
--- a/test/core/end2end/fixtures/h2_ssl_cert.c
+++ b/test/core/end2end/fixtures/h2_ssl_cert.c
@@ -331,7 +331,7 @@
   error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(1), expected_result == SUCCESS);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), expected_result == SUCCESS);
   cq_verify(cqv);
 
   grpc_call_destroy(c);
diff --git a/test/core/end2end/fixtures/h2_ssl_proxy.c b/test/core/end2end/fixtures/h2_ssl_proxy.c
index 238e6bc..eeb54b8 100644
--- a/test/core/end2end/fixtures/h2_ssl_proxy.c
+++ b/test/core/end2end/fixtures/h2_ssl_proxy.c
@@ -184,6 +184,7 @@
 static grpc_end2end_test_config configs[] = {
     {"chttp2/simple_ssl_fullstack",
      FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
+         FEATURE_MASK_SUPPORTS_REQUEST_PROXYING |
          FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS,
      chttp2_create_fixture_secure_fullstack,
      chttp2_init_client_simple_ssl_secure_fullstack,
diff --git a/test/core/end2end/fixtures/http_proxy.c b/test/core/end2end/fixtures/http_proxy.c
new file mode 100644
index 0000000..22533b9
--- /dev/null
+++ b/test/core/end2end/fixtures/http_proxy.c
@@ -0,0 +1,481 @@
+/*
+ *
+ * Copyright 2016, 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/fixtures/http_proxy.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice_buffer.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "test/core/util/port.h"
+
+struct grpc_end2end_http_proxy {
+  char* proxy_name;
+  gpr_thd_id thd;
+  grpc_tcp_server* server;
+  grpc_channel_args* channel_args;
+  gpr_mu* mu;
+  grpc_pollset* pollset;
+  gpr_atm shutdown;
+};
+
+//
+// Connection handling
+//
+
+typedef struct proxy_connection {
+  grpc_endpoint* client_endpoint;
+  grpc_endpoint* server_endpoint;
+
+  gpr_refcount refcount;
+
+  grpc_pollset_set* pollset_set;
+
+  grpc_closure on_read_request_done;
+  grpc_closure on_server_connect_done;
+  grpc_closure on_write_response_done;
+  grpc_closure on_client_read_done;
+  grpc_closure on_client_write_done;
+  grpc_closure on_server_read_done;
+  grpc_closure on_server_write_done;
+
+  gpr_slice_buffer client_read_buffer;
+  gpr_slice_buffer client_deferred_write_buffer;
+  gpr_slice_buffer client_write_buffer;
+  gpr_slice_buffer server_read_buffer;
+  gpr_slice_buffer server_deferred_write_buffer;
+  gpr_slice_buffer server_write_buffer;
+
+  grpc_http_parser http_parser;
+  grpc_http_request http_request;
+} proxy_connection;
+
+// Helper function to destroy the proxy connection.
+static void proxy_connection_unref(grpc_exec_ctx* exec_ctx,
+                                   proxy_connection* conn) {
+  if (gpr_unref(&conn->refcount)) {
+    grpc_endpoint_destroy(exec_ctx, conn->client_endpoint);
+    if (conn->server_endpoint != NULL)
+      grpc_endpoint_destroy(exec_ctx, conn->server_endpoint);
+    grpc_pollset_set_destroy(conn->pollset_set);
+    gpr_slice_buffer_destroy(&conn->client_read_buffer);
+    gpr_slice_buffer_destroy(&conn->client_deferred_write_buffer);
+    gpr_slice_buffer_destroy(&conn->client_write_buffer);
+    gpr_slice_buffer_destroy(&conn->server_read_buffer);
+    gpr_slice_buffer_destroy(&conn->server_deferred_write_buffer);
+    gpr_slice_buffer_destroy(&conn->server_write_buffer);
+    grpc_http_parser_destroy(&conn->http_parser);
+    grpc_http_request_destroy(&conn->http_request);
+    gpr_free(conn);
+  }
+}
+
+// Helper function to shut down the proxy connection.
+// Does NOT take ownership of a reference to error.
+static void proxy_connection_failed(grpc_exec_ctx* exec_ctx,
+                                    proxy_connection* conn, bool is_client,
+                                    const char* prefix, grpc_error* error) {
+  const char* msg = grpc_error_string(error);
+  gpr_log(GPR_INFO, "%s: %s", prefix, msg);
+  grpc_error_free_string(msg);
+  grpc_endpoint_shutdown(exec_ctx, conn->client_endpoint);
+  if (conn->server_endpoint != NULL)
+    grpc_endpoint_shutdown(exec_ctx, conn->server_endpoint);
+  proxy_connection_unref(exec_ctx, conn);
+}
+
+// Callback for writing proxy data to the client.
+static void on_client_write_done(grpc_exec_ctx* exec_ctx, void* arg,
+                                 grpc_error* error) {
+  proxy_connection* conn = arg;
+  if (error != GRPC_ERROR_NONE) {
+    proxy_connection_failed(exec_ctx, conn, true /* is_client */,
+                            "HTTP proxy client write", error);
+    return;
+  }
+  // Clear write buffer (the data we just wrote).
+  gpr_slice_buffer_reset_and_unref(&conn->client_write_buffer);
+  // If more data was read from the server since we started this write,
+  // write that data now.
+  if (conn->client_deferred_write_buffer.length > 0) {
+    gpr_slice_buffer_move_into(&conn->client_deferred_write_buffer,
+                               &conn->client_write_buffer);
+    grpc_endpoint_write(exec_ctx, conn->client_endpoint,
+                        &conn->client_write_buffer,
+                        &conn->on_client_write_done);
+  } else {
+    // No more writes.  Unref the connection.
+    proxy_connection_unref(exec_ctx, conn);
+  }
+}
+
+// Callback for writing proxy data to the backend server.
+static void on_server_write_done(grpc_exec_ctx* exec_ctx, void* arg,
+                                 grpc_error* error) {
+  proxy_connection* conn = arg;
+  if (error != GRPC_ERROR_NONE) {
+    proxy_connection_failed(exec_ctx, conn, false /* is_client */,
+                            "HTTP proxy server write", error);
+    return;
+  }
+  // Clear write buffer (the data we just wrote).
+  gpr_slice_buffer_reset_and_unref(&conn->server_write_buffer);
+  // If more data was read from the client since we started this write,
+  // write that data now.
+  if (conn->server_deferred_write_buffer.length > 0) {
+    gpr_slice_buffer_move_into(&conn->server_deferred_write_buffer,
+                               &conn->server_write_buffer);
+    grpc_endpoint_write(exec_ctx, conn->server_endpoint,
+                        &conn->server_write_buffer,
+                        &conn->on_server_write_done);
+  } else {
+    // No more writes.  Unref the connection.
+    proxy_connection_unref(exec_ctx, conn);
+  }
+}
+
+// Callback for reading data from the client, which will be proxied to
+// the backend server.
+static void on_client_read_done(grpc_exec_ctx* exec_ctx, void* arg,
+                                grpc_error* error) {
+  proxy_connection* conn = arg;
+  if (error != GRPC_ERROR_NONE) {
+    proxy_connection_failed(exec_ctx, conn, true /* is_client */,
+                            "HTTP proxy client read", error);
+    return;
+  }
+  // If there is already a pending write (i.e., server_write_buffer is
+  // not empty), then move the read data into server_deferred_write_buffer,
+  // and the next write will be requested in on_server_write_done(), when
+  // the current write is finished.
+  //
+  // Otherwise, move the read data into the write buffer and write it.
+  if (conn->server_write_buffer.length > 0) {
+    gpr_slice_buffer_move_into(&conn->client_read_buffer,
+                               &conn->server_deferred_write_buffer);
+  } else {
+    gpr_slice_buffer_move_into(&conn->client_read_buffer,
+                               &conn->server_write_buffer);
+    gpr_ref(&conn->refcount);
+    grpc_endpoint_write(exec_ctx, conn->server_endpoint,
+                        &conn->server_write_buffer,
+                        &conn->on_server_write_done);
+  }
+  // Read more data.
+  grpc_endpoint_read(exec_ctx, conn->client_endpoint, &conn->client_read_buffer,
+                     &conn->on_client_read_done);
+}
+
+// Callback for reading data from the backend server, which will be
+// proxied to the client.
+static void on_server_read_done(grpc_exec_ctx* exec_ctx, void* arg,
+                                grpc_error* error) {
+  proxy_connection* conn = arg;
+  if (error != GRPC_ERROR_NONE) {
+    proxy_connection_failed(exec_ctx, conn, false /* is_client */,
+                            "HTTP proxy server read", error);
+    return;
+  }
+  // If there is already a pending write (i.e., client_write_buffer is
+  // not empty), then move the read data into client_deferred_write_buffer,
+  // and the next write will be requested in on_client_write_done(), when
+  // the current write is finished.
+  //
+  // Otherwise, move the read data into the write buffer and write it.
+  if (conn->client_write_buffer.length > 0) {
+    gpr_slice_buffer_move_into(&conn->server_read_buffer,
+                               &conn->client_deferred_write_buffer);
+  } else {
+    gpr_slice_buffer_move_into(&conn->server_read_buffer,
+                               &conn->client_write_buffer);
+    gpr_ref(&conn->refcount);
+    grpc_endpoint_write(exec_ctx, conn->client_endpoint,
+                        &conn->client_write_buffer,
+                        &conn->on_client_write_done);
+  }
+  // Read more data.
+  grpc_endpoint_read(exec_ctx, conn->server_endpoint, &conn->server_read_buffer,
+                     &conn->on_server_read_done);
+}
+
+// Callback to write the HTTP response for the CONNECT request.
+static void on_write_response_done(grpc_exec_ctx* exec_ctx, void* arg,
+                                   grpc_error* error) {
+  proxy_connection* conn = arg;
+  if (error != GRPC_ERROR_NONE) {
+    proxy_connection_failed(exec_ctx, conn, true /* is_client */,
+                            "HTTP proxy write response", error);
+    return;
+  }
+  // Clear write buffer.
+  gpr_slice_buffer_reset_and_unref(&conn->client_write_buffer);
+  // Start reading from both client and server.  One of the read
+  // requests inherits our ref to conn, but we need to take a new ref
+  // for the other one.
+  gpr_ref(&conn->refcount);
+  grpc_endpoint_read(exec_ctx, conn->client_endpoint, &conn->client_read_buffer,
+                     &conn->on_client_read_done);
+  grpc_endpoint_read(exec_ctx, conn->server_endpoint, &conn->server_read_buffer,
+                     &conn->on_server_read_done);
+}
+
+// Callback to connect to the backend server specified by the HTTP
+// CONNECT request.
+static void on_server_connect_done(grpc_exec_ctx* exec_ctx, void* arg,
+                                   grpc_error* error) {
+  proxy_connection* conn = arg;
+  if (error != GRPC_ERROR_NONE) {
+    // TODO(roth): Technically, in this case, we should handle the error
+    // by returning an HTTP response to the client indicating that the
+    // connection failed.  However, for the purposes of this test code,
+    // it's fine to pretend this is a client-side error, which will
+    // cause the client connection to be dropped.
+    proxy_connection_failed(exec_ctx, conn, true /* is_client */,
+                            "HTTP proxy server connect", error);
+    return;
+  }
+  // We've established a connection, so send back a 200 response code to
+  // the client.
+  // The write callback inherits our reference to conn.
+  gpr_slice slice =
+      gpr_slice_from_copied_string("HTTP/1.0 200 connected\r\n\r\n");
+  gpr_slice_buffer_add(&conn->client_write_buffer, slice);
+  grpc_endpoint_write(exec_ctx, conn->client_endpoint,
+                      &conn->client_write_buffer,
+                      &conn->on_write_response_done);
+}
+
+// Callback to read the HTTP CONNECT request.
+// TODO(roth): Technically, for any of the failure modes handled by this
+// function, we should handle the error by returning an HTTP response to
+// the client indicating that the request failed.  However, for the purposes
+// of this test code, it's fine to pretend this is a client-side error,
+// which will cause the client connection to be dropped.
+static void on_read_request_done(grpc_exec_ctx* exec_ctx, void* arg,
+                                 grpc_error* error) {
+  proxy_connection* conn = arg;
+  if (error != GRPC_ERROR_NONE) {
+    proxy_connection_failed(exec_ctx, conn, true /* is_client */,
+                            "HTTP proxy read request", error);
+    return;
+  }
+  // Read request and feed it to the parser.
+  for (size_t i = 0; i < conn->client_read_buffer.count; ++i) {
+    if (GPR_SLICE_LENGTH(conn->client_read_buffer.slices[i]) > 0) {
+      error = grpc_http_parser_parse(&conn->http_parser,
+                                     conn->client_read_buffer.slices[i], NULL);
+      if (error != GRPC_ERROR_NONE) {
+        proxy_connection_failed(exec_ctx, conn, true /* is_client */,
+                                "HTTP proxy request parse", error);
+        GRPC_ERROR_UNREF(error);
+        return;
+      }
+    }
+  }
+  gpr_slice_buffer_reset_and_unref(&conn->client_read_buffer);
+  // If we're not done reading the request, read more data.
+  if (conn->http_parser.state != GRPC_HTTP_BODY) {
+    grpc_endpoint_read(exec_ctx, conn->client_endpoint,
+                       &conn->client_read_buffer, &conn->on_read_request_done);
+    return;
+  }
+  // Make sure we got a CONNECT request.
+  if (strcmp(conn->http_request.method, "CONNECT") != 0) {
+    char* msg;
+    gpr_asprintf(&msg, "HTTP proxy got request method %s",
+                 conn->http_request.method);
+    error = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
+    proxy_connection_failed(exec_ctx, conn, true /* is_client */,
+                            "HTTP proxy read request", error);
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  // Resolve address.
+  grpc_resolved_addresses* resolved_addresses = NULL;
+  error = grpc_blocking_resolve_address(conn->http_request.path, "80",
+                                        &resolved_addresses);
+  if (error != GRPC_ERROR_NONE) {
+    proxy_connection_failed(exec_ctx, conn, true /* is_client */,
+                            "HTTP proxy DNS lookup", error);
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  GPR_ASSERT(resolved_addresses->naddrs >= 1);
+  // Connect to requested address.
+  // The connection callback inherits our reference to conn.
+  const gpr_timespec deadline = gpr_time_add(
+      gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(10, GPR_TIMESPAN));
+  grpc_tcp_client_connect(exec_ctx, &conn->on_server_connect_done,
+                          &conn->server_endpoint, conn->pollset_set,
+                          (struct sockaddr*)&resolved_addresses->addrs[0].addr,
+                          resolved_addresses->addrs[0].len, deadline);
+  grpc_resolved_addresses_destroy(resolved_addresses);
+}
+
+static void on_accept(grpc_exec_ctx* exec_ctx, void* arg,
+                      grpc_endpoint* endpoint, grpc_pollset* accepting_pollset,
+                      grpc_tcp_server_acceptor* acceptor) {
+  grpc_end2end_http_proxy* proxy = arg;
+  // Instantiate proxy_connection.
+  proxy_connection* conn = gpr_malloc(sizeof(*conn));
+  memset(conn, 0, sizeof(*conn));
+  conn->client_endpoint = endpoint;
+  gpr_ref_init(&conn->refcount, 1);
+  conn->pollset_set = grpc_pollset_set_create();
+  grpc_pollset_set_add_pollset(exec_ctx, conn->pollset_set, proxy->pollset);
+  grpc_closure_init(&conn->on_read_request_done, on_read_request_done, conn);
+  grpc_closure_init(&conn->on_server_connect_done, on_server_connect_done,
+                    conn);
+  grpc_closure_init(&conn->on_write_response_done, on_write_response_done,
+                    conn);
+  grpc_closure_init(&conn->on_client_read_done, on_client_read_done, conn);
+  grpc_closure_init(&conn->on_client_write_done, on_client_write_done, conn);
+  grpc_closure_init(&conn->on_server_read_done, on_server_read_done, conn);
+  grpc_closure_init(&conn->on_server_write_done, on_server_write_done, conn);
+  gpr_slice_buffer_init(&conn->client_read_buffer);
+  gpr_slice_buffer_init(&conn->client_deferred_write_buffer);
+  gpr_slice_buffer_init(&conn->client_write_buffer);
+  gpr_slice_buffer_init(&conn->server_read_buffer);
+  gpr_slice_buffer_init(&conn->server_deferred_write_buffer);
+  gpr_slice_buffer_init(&conn->server_write_buffer);
+  grpc_http_parser_init(&conn->http_parser, GRPC_HTTP_REQUEST,
+                        &conn->http_request);
+  grpc_endpoint_read(exec_ctx, conn->client_endpoint, &conn->client_read_buffer,
+                     &conn->on_read_request_done);
+}
+
+//
+// Proxy class
+//
+
+static void thread_main(void* arg) {
+  grpc_end2end_http_proxy* proxy = arg;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  do {
+    const gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
+    const gpr_timespec deadline =
+        gpr_time_add(now, gpr_time_from_seconds(1, GPR_TIMESPAN));
+    grpc_pollset_worker* worker = NULL;
+    gpr_mu_lock(proxy->mu);
+    GRPC_LOG_IF_ERROR(
+        "grpc_pollset_work",
+        grpc_pollset_work(&exec_ctx, proxy->pollset, &worker, now, deadline));
+    gpr_mu_unlock(proxy->mu);
+    grpc_exec_ctx_flush(&exec_ctx);
+  } while (!gpr_atm_acq_load(&proxy->shutdown));
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+grpc_end2end_http_proxy* grpc_end2end_http_proxy_create() {
+  grpc_end2end_http_proxy* proxy = gpr_malloc(sizeof(*proxy));
+  memset(proxy, 0, sizeof(*proxy));
+  // Construct proxy address.
+  const int proxy_port = grpc_pick_unused_port_or_die();
+  gpr_join_host_port(&proxy->proxy_name, "localhost", proxy_port);
+  gpr_log(GPR_INFO, "Proxy address: %s", proxy->proxy_name);
+  // Create TCP server.
+  proxy->channel_args = grpc_channel_args_copy(NULL);
+  grpc_error* error =
+      grpc_tcp_server_create(NULL, proxy->channel_args, &proxy->server);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
+  // Bind to port.
+  struct sockaddr_in addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  grpc_sockaddr_set_port((struct sockaddr*)&addr, proxy_port);
+  int port;
+  error = grpc_tcp_server_add_port(proxy->server, (struct sockaddr*)&addr,
+                                   sizeof(addr), &port);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
+  GPR_ASSERT(port == proxy_port);
+  // Start server.
+  proxy->pollset = gpr_malloc(grpc_pollset_size());
+  grpc_pollset_init(proxy->pollset, &proxy->mu);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_tcp_server_start(&exec_ctx, proxy->server, &proxy->pollset, 1, on_accept,
+                        proxy);
+  grpc_exec_ctx_finish(&exec_ctx);
+  // Start proxy thread.
+  gpr_thd_options opt = gpr_thd_options_default();
+  gpr_thd_options_set_joinable(&opt);
+  GPR_ASSERT(gpr_thd_new(&proxy->thd, thread_main, proxy, &opt));
+  return proxy;
+}
+
+static void destroy_pollset(grpc_exec_ctx* exec_ctx, void* arg,
+                            grpc_error* error) {
+  grpc_pollset* pollset = arg;
+  grpc_pollset_destroy(pollset);
+  gpr_free(pollset);
+}
+
+void grpc_end2end_http_proxy_destroy(grpc_end2end_http_proxy* proxy) {
+  gpr_atm_rel_store(&proxy->shutdown, 1);  // Signal proxy thread to shutdown.
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  gpr_thd_join(proxy->thd);
+  grpc_tcp_server_shutdown_listeners(&exec_ctx, proxy->server);
+  grpc_tcp_server_unref(&exec_ctx, proxy->server);
+  gpr_free(proxy->proxy_name);
+  grpc_channel_args_destroy(proxy->channel_args);
+  grpc_closure destroyed;
+  grpc_closure_init(&destroyed, destroy_pollset, proxy->pollset);
+  grpc_pollset_shutdown(&exec_ctx, proxy->pollset, &destroyed);
+  gpr_free(proxy);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+const char* grpc_end2end_http_proxy_get_proxy_name(
+    grpc_end2end_http_proxy* proxy) {
+  return proxy->proxy_name;
+}
diff --git a/test/core/end2end/fixtures/http_proxy.h b/test/core/end2end/fixtures/http_proxy.h
new file mode 100644
index 0000000..cd47b43
--- /dev/null
+++ b/test/core/end2end/fixtures/http_proxy.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2016, 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.
+ *
+ */
+
+typedef struct grpc_end2end_http_proxy grpc_end2end_http_proxy;
+
+grpc_end2end_http_proxy* grpc_end2end_http_proxy_create();
+
+void grpc_end2end_http_proxy_destroy(grpc_end2end_http_proxy* proxy);
+
+const char* grpc_end2end_http_proxy_get_proxy_name(
+    grpc_end2end_http_proxy* proxy);
diff --git a/test/core/end2end/fuzzers/api_fuzzer.c b/test/core/end2end/fuzzers/api_fuzzer.c
index 13b8bf7..96ea82d 100644
--- a/test/core/end2end/fuzzers/api_fuzzer.c
+++ b/test/core/end2end/fuzzers/api_fuzzer.c
@@ -258,7 +258,7 @@
     grpc_transport *transport =
         grpc_create_chttp2_transport(exec_ctx, NULL, server, 0);
     grpc_server_setup_transport(exec_ctx, g_server, transport, NULL, NULL);
-    grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0);
+    grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL);
 
     grpc_exec_ctx_sched(exec_ctx, fc->closure, GRPC_ERROR_NONE, NULL);
   } else {
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/22967e8ed837f03b76a980cc1d25054fb84b40e9 b/test/core/end2end/fuzzers/api_fuzzer_corpus/22967e8ed837f03b76a980cc1d25054fb84b40e9
new file mode 100644
index 0000000..1aa57b9
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/22967e8ed837f03b76a980cc1d25054fb84b40e9
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/3f464011f8620f227309f6b2c84df6fffb8ed962 b/test/core/end2end/fuzzers/api_fuzzer_corpus/3f464011f8620f227309f6b2c84df6fffb8ed962
new file mode 100644
index 0000000..385a724
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/3f464011f8620f227309f6b2c84df6fffb8ed962
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/crash-15070b2a2719ed8a6cbbaac25da02b7085993648 b/test/core/end2end/fuzzers/api_fuzzer_corpus/crash-15070b2a2719ed8a6cbbaac25da02b7085993648
new file mode 100644
index 0000000..e21c7c6
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/crash-15070b2a2719ed8a6cbbaac25da02b7085993648
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer.c b/test/core/end2end/fuzzers/client_fuzzer.c
index 79b23d7..00e650a 100644
--- a/test/core/end2end/fuzzers/client_fuzzer.c
+++ b/test/core/end2end/fuzzers/client_fuzzer.c
@@ -63,7 +63,7 @@
   grpc_completion_queue *cq = grpc_completion_queue_create(NULL);
   grpc_transport *transport =
       grpc_create_chttp2_transport(&exec_ctx, NULL, mock_endpoint, 1);
-  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
 
   grpc_channel *channel = grpc_channel_create(
       &exec_ctx, "test-target", NULL, GRPC_CLIENT_DIRECT_CHANNEL, transport);
diff --git a/test/core/end2end/fuzzers/hpack.dictionary b/test/core/end2end/fuzzers/hpack.dictionary
index 097e9a8..a93bccf 100644
--- a/test/core/end2end/fuzzers/hpack.dictionary
+++ b/test/core/end2end/fuzzers/hpack.dictionary
@@ -21,8 +21,6 @@
 "\x0A:authority"
 "\x0Dauthorization"
 "\x0Dcache-control"
-"\x0Acensus-bin"
-"\x11census-binary-bin"
 "\x13content-disposition"
 "\x10content-encoding"
 "\x10content-language"
@@ -45,8 +43,11 @@
 "\x0Dgrpc-encoding"
 "\x1Egrpc-internal-encoding-request"
 "\x0Cgrpc-message"
+"\x10grpc-payload-bin"
+"\x0Egrpc-stats-bin"
 "\x0Bgrpc-status"
 "\x0Cgrpc-timeout"
+"\x10grpc-tracing-bin"
 "\x04gzip"
 "\x0Dgzip, deflate"
 "\x04host"
@@ -63,7 +64,8 @@
 "\x13if-unmodified-since"
 "\x0Dlast-modified"
 "\x04link"
-"\x0Eload-reporting"
+"\x16load-reporting-initial"
+"\x17load-reporting-trailing"
 "\x08location"
 "\x0Cmax-forwards"
 "\x07:method"
@@ -137,7 +139,8 @@
 "\x00\x13if-unmodified-since\x00"
 "\x00\x0Dlast-modified\x00"
 "\x00\x04link\x00"
-"\x00\x0Eload-reporting\x00"
+"\x00\x16load-reporting-initial\x00"
+"\x00\x17load-reporting-trailing\x00"
 "\x00\x08location\x00"
 "\x00\x0Cmax-forwards\x00"
 "\x00\x07:method\x03GET"
diff --git a/test/core/end2end/fuzzers/server_fuzzer.c b/test/core/end2end/fuzzers/server_fuzzer.c
index 80f568a..79eaad7 100644
--- a/test/core/end2end/fuzzers/server_fuzzer.c
+++ b/test/core/end2end/fuzzers/server_fuzzer.c
@@ -71,7 +71,7 @@
   grpc_transport *transport =
       grpc_create_chttp2_transport(&exec_ctx, NULL, mock_endpoint, 0);
   grpc_server_setup_transport(&exec_ctx, server, transport, NULL, NULL);
-  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+  grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
 
   grpc_call *call1 = NULL;
   grpc_call_details call_details1;
diff --git a/test/core/end2end/gen_build_yaml.py b/test/core/end2end/gen_build_yaml.py
index fb72754..78b37ef 100755
--- a/test/core/end2end/gen_build_yaml.py
+++ b/test/core/end2end/gen_build_yaml.py
@@ -53,13 +53,14 @@
 END2END_FIXTURES = {
     'h2_compress': default_unsecure_fixture_options,
     'h2_census': default_unsecure_fixture_options,
+    'h2_load_reporting': default_unsecure_fixture_options,
     'h2_fakesec': default_secure_fixture_options._replace(ci_mac=False),
     'h2_fd': fd_unsecure_fixture_options,
     'h2_full': default_unsecure_fixture_options,
     'h2_full+pipe': default_unsecure_fixture_options._replace(
         platforms=['linux']),
     'h2_full+trace': default_unsecure_fixture_options._replace(tracing=True),
-    'h2_loadreporting': default_unsecure_fixture_options,
+    'h2_http_proxy': default_unsecure_fixture_options._replace(ci_mac=False),
     'h2_oauth2': default_secure_fixture_options._replace(ci_mac=False),
     'h2_proxy': default_unsecure_fixture_options._replace(includes_proxy=True,
                                                           ci_mac=False),
@@ -102,6 +103,7 @@
     'disappearing_server': connectivity_test_options,
     'empty_batch': default_test_options,
     'filter_causes_close': default_test_options,
+    'filter_call_init_fails': default_test_options,
     'graceful_server_shutdown': default_test_options._replace(cpu_cost=LOWCPU),
     'hpack_size': default_test_options._replace(proxyable=False,
                                                 traceable=False),
@@ -113,8 +115,10 @@
     'max_message_length': default_test_options,
     'negative_deadline': default_test_options,
     'network_status_change': default_test_options,
+    'no_logging': default_test_options._replace(traceable=False),
     'no_op': default_test_options,
     'payload': default_test_options,
+    'load_reporting_hook': default_test_options,
     'ping_pong_streaming': default_test_options,
     'ping': connectivity_test_options._replace(proxyable=False),
     'registered_call': default_test_options,
@@ -124,6 +128,7 @@
     'server_finishes_request': default_test_options,
     'shutdown_finishes_calls': default_test_options,
     'shutdown_finishes_tags': default_test_options,
+    'simple_cacheable_request': default_test_options,
     'simple_delayed_request': connectivity_test_options,
     'simple_metadata': default_test_options,
     'simple_request': default_test_options,
@@ -221,7 +226,7 @@
               'name': '%s_nosec_test' % f,
               'build': 'test',
               'language': 'c',
-              'secure': 'no',
+              'secure': False,
               'src': ['test/core/end2end/fixtures/%s.c' % f],
               'run': False,
               'platforms': END2END_FIXTURES[f].platforms,
diff --git a/test/core/end2end/goaway_server_test.c b/test/core/end2end/goaway_server_test.c
index 67cc24c..a8c7b2b 100644
--- a/test/core/end2end/goaway_server_test.c
+++ b/test/core/end2end/goaway_server_test.c
@@ -175,8 +175,8 @@
   set_resolve_port(port1);
 
   /* first call should now start */
-  cq_expect_completion(cqv, tag(0x101), 1);
-  cq_expect_completion(cqv, tag(0x301), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0x101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0x301), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(GRPC_CHANNEL_READY ==
@@ -200,7 +200,7 @@
    * we should see a connectivity change and then nothing */
   set_resolve_port(-1);
   grpc_server_shutdown_and_notify(server1, cq, tag(0xdead1));
-  cq_expect_completion(cqv, tag(0x9999), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0x9999), 1);
   cq_verify(cqv);
   cq_verify_empty(cqv);
 
@@ -250,8 +250,8 @@
                                       &request_metadata2, cq, cq, tag(0x401)));
 
   /* second call should now start */
-  cq_expect_completion(cqv, tag(0x201), 1);
-  cq_expect_completion(cqv, tag(0x401), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0x201), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0x401), 1);
   cq_verify(cqv);
 
   /* listen for close on the server call to probe for finishing */
@@ -273,12 +273,12 @@
   grpc_call_cancel(call2, NULL);
 
   /* now everything else should finish */
-  cq_expect_completion(cqv, tag(0x102), 1);
-  cq_expect_completion(cqv, tag(0x202), 1);
-  cq_expect_completion(cqv, tag(0x302), 1);
-  cq_expect_completion(cqv, tag(0x402), 1);
-  cq_expect_completion(cqv, tag(0xdead1), 1);
-  cq_expect_completion(cqv, tag(0xdead2), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0x102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0x202), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0x302), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0x402), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0xdead1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0xdead2), 1);
   cq_verify(cqv);
 
   grpc_call_destroy(call1);
diff --git a/test/core/end2end/invalid_call_argument_test.c b/test/core/end2end/invalid_call_argument_test.c
index 3545660..2b9904a 100644
--- a/test/core/end2end/invalid_call_argument_test.c
+++ b/test/core/end2end/invalid_call_argument_test.c
@@ -116,8 +116,8 @@
                                         &g_state.call_details,
                                         &g_state.server_initial_metadata_recv,
                                         g_state.cq, g_state.cq, tag(101)));
-    cq_expect_completion(g_state.cqv, tag(101), 1);
-    cq_expect_completion(g_state.cqv, tag(1), 1);
+    CQ_EXPECT_COMPLETION(g_state.cqv, tag(101), 1);
+    CQ_EXPECT_COMPLETION(g_state.cqv, tag(1), 1);
     cq_verify(g_state.cqv);
   }
 }
@@ -191,7 +191,7 @@
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(g_state.call, g_state.ops,
                                                    (size_t)(op - g_state.ops),
                                                    tag(1), NULL));
-  cq_expect_completion(g_state.cqv, tag(1), 0);
+  CQ_EXPECT_COMPLETION(g_state.cqv, tag(1), 0);
   cq_verify(g_state.cqv);
 
   op = g_state.ops;
@@ -312,7 +312,7 @@
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(g_state.call, g_state.ops,
                                                    (size_t)(op - g_state.ops),
                                                    tag(1), NULL));
-  cq_expect_completion(g_state.cqv, tag(1), 0);
+  CQ_EXPECT_COMPLETION(g_state.cqv, tag(1), 0);
   cq_verify(g_state.cqv);
   op = g_state.ops;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
@@ -405,7 +405,7 @@
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(g_state.call, g_state.ops,
                                                    (size_t)(op - g_state.ops),
                                                    tag(1), NULL));
-  cq_expect_completion(g_state.cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(g_state.cqv, tag(1), 1);
   cq_verify(g_state.cqv);
 
   op = g_state.ops;
diff --git a/test/core/end2end/no_server_test.c b/test/core/end2end/no_server_test.c
index 08af382..03ff70a 100644
--- a/test/core/end2end/no_server_test.c
+++ b/test/core/end2end/no_server_test.c
@@ -86,7 +86,7 @@
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(
                                  call, ops, (size_t)(op - ops), tag(1), NULL));
   /* verify that all tags get completed */
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_DEADLINE_EXCEEDED);
diff --git a/test/core/end2end/tests/bad_hostname.c b/test/core/end2end/tests/bad_hostname.c
index c9663c2..e0c7ac7 100644
--- a/test/core/end2end/tests/bad_hostname.c
+++ b/test/core/end2end/tests/bad_hostname.c
@@ -45,8 +45,6 @@
 #include "src/core/lib/support/string.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -150,7 +148,7 @@
   error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_INTERNAL);
diff --git a/test/core/end2end/tests/binary_metadata.c b/test/core/end2end/tests/binary_metadata.c
index 3dd2612..6b105de 100644
--- a/test/core/end2end/tests/binary_metadata.c
+++ b/test/core/end2end/tests/binary_metadata.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -199,7 +197,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -218,7 +216,7 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -243,8 +241,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(103), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
diff --git a/test/core/end2end/tests/call_creds.c b/test/core/end2end/tests/call_creds.c
index 694a0aa..981c0fc 100644
--- a/test/core/end2end/tests/call_creds.c
+++ b/test/core/end2end/tests/call_creds.c
@@ -53,8 +53,6 @@
 
 typedef enum { NONE, OVERRIDE, DESTROY } override_mode;
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -234,7 +232,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
   s_auth_context = grpc_call_auth_context(s);
   GPR_ASSERT(s_auth_context != NULL);
@@ -264,7 +262,7 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -289,8 +287,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(103), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
@@ -450,7 +448,7 @@
   error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
   GPR_ASSERT(error == GRPC_CALL_OK);
 
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNAUTHENTICATED);
diff --git a/test/core/end2end/tests/cancel_after_accept.c b/test/core/end2end/tests/cancel_after_accept.c
index 51c13da..b4ac3b3 100644
--- a/test/core/end2end/tests/cancel_after_accept.c
+++ b/test/core/end2end/tests/cancel_after_accept.c
@@ -44,8 +44,6 @@
 #include "test/core/end2end/cq_verifier.h"
 #include "test/core/end2end/tests/cancel_test_helpers.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -172,7 +170,7 @@
   error = grpc_server_request_call(f.server, &s, &call_details,
                                    &request_metadata_recv, f.cq, f.cq, tag(2));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(2), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(2), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -202,8 +200,8 @@
 
   GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c, NULL));
 
-  cq_expect_completion(cqv, tag(3), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(3), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == mode.expect_status || status == GRPC_STATUS_INTERNAL);
diff --git a/test/core/end2end/tests/cancel_after_client_done.c b/test/core/end2end/tests/cancel_after_client_done.c
index 2b5a409..5adc71e 100644
--- a/test/core/end2end/tests/cancel_after_client_done.c
+++ b/test/core/end2end/tests/cancel_after_client_done.c
@@ -44,8 +44,6 @@
 #include "test/core/end2end/cq_verifier.h"
 #include "test/core/end2end/tests/cancel_test_helpers.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -176,7 +174,7 @@
   error = grpc_server_request_call(f.server, &s, &call_details,
                                    &request_metadata_recv, f.cq, f.cq, tag(2));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(2), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(2), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -206,8 +204,8 @@
 
   GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c, NULL));
 
-  cq_expect_completion(cqv, tag(3), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(3), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == mode.expect_status || status == GRPC_STATUS_INTERNAL);
diff --git a/test/core/end2end/tests/cancel_after_invoke.c b/test/core/end2end/tests/cancel_after_invoke.c
index 85fbe9d..85d8799 100644
--- a/test/core/end2end/tests/cancel_after_invoke.c
+++ b/test/core/end2end/tests/cancel_after_invoke.c
@@ -44,8 +44,6 @@
 #include "test/core/end2end/cq_verifier.h"
 #include "test/core/end2end/tests/cancel_test_helpers.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -170,7 +168,7 @@
 
   GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c, NULL));
 
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == mode.expect_status || status == GRPC_STATUS_INTERNAL);
diff --git a/test/core/end2end/tests/cancel_before_invoke.c b/test/core/end2end/tests/cancel_before_invoke.c
index d99062c..c570914 100644
--- a/test/core/end2end/tests/cancel_before_invoke.c
+++ b/test/core/end2end/tests/cancel_before_invoke.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -168,7 +166,7 @@
   error = grpc_call_start_batch(c, ops, test_ops, tag(1), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_CANCELLED);
diff --git a/test/core/end2end/tests/cancel_in_a_vacuum.c b/test/core/end2end/tests/cancel_in_a_vacuum.c
index 0c893b5..3b03616 100644
--- a/test/core/end2end/tests/cancel_in_a_vacuum.c
+++ b/test/core/end2end/tests/cancel_in_a_vacuum.c
@@ -44,8 +44,6 @@
 #include "test/core/end2end/cq_verifier.h"
 #include "test/core/end2end/tests/cancel_test_helpers.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
diff --git a/test/core/end2end/tests/cancel_with_status.c b/test/core/end2end/tests/cancel_with_status.c
index 673c705..e65390a 100644
--- a/test/core/end2end/tests/cancel_with_status.c
+++ b/test/core/end2end/tests/cancel_with_status.c
@@ -45,8 +45,6 @@
 #include "src/core/lib/support/string.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -152,7 +150,7 @@
 
   grpc_call_cancel_with_status(c, GRPC_STATUS_UNIMPLEMENTED, "xyz", NULL);
 
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
diff --git a/test/core/end2end/tests/compressed_payload.c b/test/core/end2end/tests/compressed_payload.c
index ec5c012..1724da4 100644
--- a/test/core/end2end/tests/compressed_payload.c
+++ b/test/core/end2end/tests/compressed_payload.c
@@ -50,8 +50,6 @@
 #include "src/core/lib/surface/call_test_only.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -198,7 +196,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), true);
   cq_verify(cqv);
 
   op = ops;
@@ -215,7 +213,7 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), false);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), false);
 
   op = ops;
   op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
@@ -226,8 +224,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(103), true);
-  cq_expect_completion(cqv, tag(1), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
   cq_verify(cqv);
 
   /* call was cancelled (closed) ... */
@@ -361,7 +359,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(100));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(100), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(100), true);
   cq_verify(cqv);
 
   GPR_ASSERT(GPR_BITCOUNT(grpc_call_test_only_get_encodings_accepted_by_peer(
@@ -421,7 +419,7 @@
     op++;
     error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
     GPR_ASSERT(GRPC_CALL_OK == error);
-    cq_expect_completion(cqv, tag(102), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
     cq_verify(cqv);
 
     GPR_ASSERT(request_payload_recv->type == GRPC_BB_RAW);
@@ -438,8 +436,8 @@
     op++;
     error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
     GPR_ASSERT(GRPC_CALL_OK == error);
-    cq_expect_completion(cqv, tag(103), 1);
-    cq_expect_completion(cqv, tag(2), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(2), 1);
     cq_verify(cqv);
 
     GPR_ASSERT(response_payload_recv->type == GRPC_BB_RAW);
@@ -484,10 +482,10 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(104), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(1), 1);
-  cq_expect_completion(cqv, tag(3), 1);
-  cq_expect_completion(cqv, tag(101), 1);
-  cq_expect_completion(cqv, tag(104), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(3), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(104), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
diff --git a/test/core/end2end/tests/connectivity.c b/test/core/end2end/tests/connectivity.c
index 5775976..260297e 100644
--- a/test/core/end2end/tests/connectivity.c
+++ b/test/core/end2end/tests/connectivity.c
@@ -102,7 +102,7 @@
                                         f.cq, tag(2));
 
   /* and now the watch should trigger */
-  cq_expect_completion(cqv, tag(2), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(2), 1);
   cq_verify(cqv);
   state = grpc_channel_check_connectivity_state(f.client, 0);
   GPR_ASSERT(state == GRPC_CHANNEL_TRANSIENT_FAILURE ||
@@ -112,7 +112,7 @@
   grpc_channel_watch_connectivity_state(f.client, GRPC_CHANNEL_CONNECTING,
                                         GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3),
                                         f.cq, tag(3));
-  cq_expect_completion(cqv, tag(3), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(3), 1);
   cq_verify(cqv);
   state = grpc_channel_check_connectivity_state(f.client, 0);
   GPR_ASSERT(state == GRPC_CHANNEL_TRANSIENT_FAILURE ||
@@ -130,7 +130,7 @@
   while (state != GRPC_CHANNEL_READY) {
     grpc_channel_watch_connectivity_state(
         f.client, state, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3), f.cq, tag(4));
-    cq_expect_completion(cqv, tag(4), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(4), 1);
     cq_verify(cqv);
     state = grpc_channel_check_connectivity_state(f.client, 0);
     GPR_ASSERT(state == GRPC_CHANNEL_READY ||
@@ -148,8 +148,8 @@
 
   grpc_server_shutdown_and_notify(f.server, f.cq, tag(0xdead));
 
-  cq_expect_completion(cqv, tag(5), 1);
-  cq_expect_completion(cqv, tag(0xdead), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(5), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0xdead), 1);
   cq_verify(cqv);
   state = grpc_channel_check_connectivity_state(f.client, 0);
   GPR_ASSERT(state == GRPC_CHANNEL_TRANSIENT_FAILURE ||
diff --git a/test/core/end2end/tests/default_host.c b/test/core/end2end/tests/default_host.c
index 728ee59..208e316 100644
--- a/test/core/end2end/tests/default_host.c
+++ b/test/core/end2end/tests/default_host.c
@@ -45,8 +45,6 @@
 #include "src/core/lib/support/string.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -162,7 +160,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(error == GRPC_CALL_OK);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   peer = grpc_call_get_peer(s);
@@ -196,8 +194,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(error == GRPC_CALL_OK);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
diff --git a/test/core/end2end/tests/disappearing_server.c b/test/core/end2end/tests/disappearing_server.c
index 536fbd0..a0059b9 100644
--- a/test/core/end2end/tests/disappearing_server.c
+++ b/test/core/end2end/tests/disappearing_server.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static gpr_timespec n_seconds_time(int n) {
@@ -139,7 +137,7 @@
       grpc_server_request_call(f->server, &s, &call_details,
                                &request_metadata_recv, f->cq, f->cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   /* should be able to shut down the server early
@@ -168,9 +166,9 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(1), 1);
-  cq_expect_completion(cqv, tag(1000), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1000), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
diff --git a/test/core/end2end/tests/empty_batch.c b/test/core/end2end/tests/empty_batch.c
index c05b919..ac53e18 100644
--- a/test/core/end2end/tests/empty_batch.c
+++ b/test/core/end2end/tests/empty_batch.c
@@ -45,8 +45,6 @@
 #include "src/core/lib/support/string.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -112,7 +110,7 @@
 
   error = grpc_call_start_batch(c, op, 0, tag(1), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   grpc_call_destroy(c);
diff --git a/test/core/end2end/tests/filter_call_init_fails.c b/test/core/end2end/tests/filter_call_init_fails.c
new file mode 100644
index 0000000..a70f50a
--- /dev/null
+++ b/test/core/end2end/tests/filter_call_init_fails.c
@@ -0,0 +1,273 @@
+/*
+ *
+ * Copyright 2016, 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 <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/channel/channel_stack_builder.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static bool g_enable_filter = false;
+
+static void *tag(intptr_t t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char *test_name,
+                                            grpc_channel_args *client_args,
+                                            grpc_channel_args *server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+  return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(n);
+}
+
+static gpr_timespec five_seconds_time(void) { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_time(), NULL);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(
+                 f->cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+}
+
+// Simple request via a server filter that always fails to initialize
+// the call.
+static void test_request(grpc_end2end_test_config config) {
+  grpc_call *c;
+  grpc_call *s;
+  gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
+  grpc_byte_buffer *request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  gpr_timespec deadline = five_seconds_time();
+  grpc_end2end_test_fixture f =
+      begin_test(config, "filter_call_init_fails", NULL, NULL);
+  cq_verifier *cqv = cq_verifier_create(f.cq);
+  grpc_op ops[6];
+  grpc_op *op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_byte_buffer *request_payload_recv = NULL;
+  grpc_call_details call_details;
+  grpc_status_code status;
+  grpc_call_error error;
+  char *details = NULL;
+  size_t details_capacity = 0;
+
+  c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+                               "/foo", "foo.test.google.fr", deadline, NULL);
+  GPR_ASSERT(c);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->data.send_initial_metadata.metadata = NULL;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message = request_payload;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_PERMISSION_DENIED);
+  GPR_ASSERT(0 == strcmp(details, "access denied"));
+
+  gpr_free(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+
+  grpc_call_destroy(c);
+
+  cq_verifier_destroy(cqv);
+
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+/*******************************************************************************
+ * Test filter - always fails to initialize a call
+ */
+
+static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  grpc_call_element_args *args) {
+  return grpc_error_set_int(GRPC_ERROR_CREATE("access denied"),
+                            GRPC_ERROR_INT_GRPC_STATUS,
+                            GRPC_STATUS_PERMISSION_DENIED);
+}
+
+static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+                              const grpc_call_final_info *final_info,
+                              void *and_free_memory) {}
+
+static void init_channel_elem(grpc_exec_ctx *exec_ctx,
+                              grpc_channel_element *elem,
+                              grpc_channel_element_args *args) {}
+
+static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
+                                 grpc_channel_element *elem) {}
+
+static const grpc_channel_filter test_filter = {
+    grpc_call_next_op,
+    grpc_channel_next_op,
+    0,
+    init_call_elem,
+    grpc_call_stack_ignore_set_pollset_or_pollset_set,
+    destroy_call_elem,
+    0,
+    init_channel_elem,
+    destroy_channel_elem,
+    grpc_call_next_get_peer,
+    "filter_call_init_fails"};
+
+/*******************************************************************************
+ * Registration
+ */
+
+static bool maybe_add_filter(grpc_channel_stack_builder *builder, void *arg) {
+  if (g_enable_filter) {
+    // Want to add the filter as close to the end as possible, to make
+    // sure that all of the filters work well together.  However, we
+    // can't add it at the very end, because the connected channel filter
+    // must be the last one.  So we add it right before the last one.
+    grpc_channel_stack_builder_iterator *it =
+        grpc_channel_stack_builder_create_iterator_at_last(builder);
+    GPR_ASSERT(grpc_channel_stack_builder_move_prev(it));
+    const bool retval = grpc_channel_stack_builder_add_filter_before(
+        it, &test_filter, NULL, NULL);
+    grpc_channel_stack_builder_iterator_destroy(it);
+    return retval;
+  } else {
+    return true;
+  }
+}
+
+static void init_plugin(void) {
+  grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX,
+                                   maybe_add_filter, NULL);
+}
+
+static void destroy_plugin(void) {}
+
+void filter_call_init_fails(grpc_end2end_test_config config) {
+  g_enable_filter = true;
+  test_request(config);
+  g_enable_filter = false;
+}
+
+void filter_call_init_fails_pre_init(void) {
+  grpc_register_plugin(init_plugin, destroy_plugin);
+}
diff --git a/test/core/end2end/tests/filter_causes_close.c b/test/core/end2end/tests/filter_causes_close.c
index 526c05c..cdf8683 100644
--- a/test/core/end2end/tests/filter_causes_close.c
+++ b/test/core/end2end/tests/filter_causes_close.c
@@ -46,8 +46,6 @@
 #include "src/core/lib/surface/channel_init.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static bool g_enable_filter = false;
 
 static void *tag(intptr_t t) { return (void *)t; }
@@ -172,7 +170,7 @@
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_PERMISSION_DENIED);
@@ -233,11 +231,14 @@
   grpc_call_next_op(exec_ctx, elem, op);
 }
 
-static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {}
+static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
+                                  grpc_call_element *elem,
+                                  grpc_call_element_args *args) {
+  return GRPC_ERROR_NONE;
+}
 
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                              const grpc_call_stats *stats,
+                              const grpc_call_final_info *final_info,
                               void *and_free_memory) {}
 
 static void init_channel_elem(grpc_exec_ctx *exec_ctx,
diff --git a/test/core/end2end/tests/graceful_server_shutdown.c b/test/core/end2end/tests/graceful_server_shutdown.c
index f527b86..29347a0 100644
--- a/test/core/end2end/tests/graceful_server_shutdown.c
+++ b/test/core/end2end/tests/graceful_server_shutdown.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -154,7 +152,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   /* shutdown and destroy the server */
@@ -183,9 +181,9 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(0xdead), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0xdead), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   grpc_call_destroy(s);
diff --git a/test/core/end2end/tests/high_initial_seqno.c b/test/core/end2end/tests/high_initial_seqno.c
index db45f5e..dab5270 100644
--- a/test/core/end2end/tests/high_initial_seqno.c
+++ b/test/core/end2end/tests/high_initial_seqno.c
@@ -47,8 +47,6 @@
 #include "src/core/lib/support/string.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -159,7 +157,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -184,8 +182,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
diff --git a/test/core/end2end/tests/hpack_size.c b/test/core/end2end/tests/hpack_size.c
index 7ff0461..fb00ae4 100644
--- a/test/core/end2end/tests/hpack_size.c
+++ b/test/core/end2end/tests/hpack_size.c
@@ -310,7 +310,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -335,8 +335,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
diff --git a/test/core/end2end/tests/idempotent_request.c b/test/core/end2end/tests/idempotent_request.c
index dfedcfe..65f6dd0 100644
--- a/test/core/end2end/tests/idempotent_request.c
+++ b/test/core/end2end/tests/idempotent_request.c
@@ -45,8 +45,6 @@
 #include "src/core/lib/support/string.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -163,7 +161,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   peer = grpc_call_get_peer(s);
@@ -197,8 +195,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
diff --git a/test/core/end2end/tests/invoke_large_request.c b/test/core/end2end/tests/invoke_large_request.c
index 9c9ca95..1df237c 100644
--- a/test/core/end2end/tests/invoke_large_request.c
+++ b/test/core/end2end/tests/invoke_large_request.c
@@ -39,12 +39,11 @@
 #include <grpc/byte_buffer.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 #include <grpc/support/time.h>
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -101,9 +100,25 @@
   return slice;
 }
 
-static void test_invoke_large_request(grpc_end2end_test_config config) {
+static void test_invoke_large_request(grpc_end2end_test_config config,
+                                      int max_frame_size, int lookahead_bytes) {
+  char *name;
+  gpr_asprintf(&name,
+               "test_invoke_large_request:max_frame_size=%d:lookahead_bytes=%d",
+               max_frame_size, lookahead_bytes);
+
+  grpc_arg args[2];
+  args[0].type = GRPC_ARG_INTEGER;
+  args[0].key = GRPC_ARG_HTTP2_MAX_FRAME_SIZE;
+  args[0].value.integer = max_frame_size;
+  args[1].type = GRPC_ARG_INTEGER;
+  args[1].key = GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES;
+  args[1].value.integer = lookahead_bytes;
+  grpc_channel_args channel_args = {GPR_ARRAY_SIZE(args), args};
+
   grpc_end2end_test_fixture f =
-      begin_test(config, "test_invoke_large_request", NULL, NULL);
+      begin_test(config, name, &channel_args, &channel_args);
+  gpr_free(name);
 
   gpr_slice request_payload_slice = large_slice();
   gpr_slice response_payload_slice = large_slice();
@@ -179,7 +194,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -197,7 +212,7 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -222,8 +237,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(103), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
@@ -255,7 +270,26 @@
 }
 
 void invoke_large_request(grpc_end2end_test_config config) {
-  test_invoke_large_request(config);
+  test_invoke_large_request(config, 16384, 65536);
+  test_invoke_large_request(config, 32768, 65536);
+
+  test_invoke_large_request(config, 1000000 - 1, 65536);
+  test_invoke_large_request(config, 1000000, 65536);
+  test_invoke_large_request(config, 1000000 + 1, 65536);
+  test_invoke_large_request(config, 1000000 + 2, 65536);
+  test_invoke_large_request(config, 1000000 + 3, 65536);
+  test_invoke_large_request(config, 1000000 + 4, 65536);
+  test_invoke_large_request(config, 1000000 + 5, 65536);
+  test_invoke_large_request(config, 1000000 + 6, 65536);
+
+  test_invoke_large_request(config, 1000000 - 1, 2000000);
+  test_invoke_large_request(config, 1000000, 2000000);
+  test_invoke_large_request(config, 1000000 + 1, 2000000);
+  test_invoke_large_request(config, 1000000 + 2, 2000000);
+  test_invoke_large_request(config, 1000000 + 3, 2000000);
+  test_invoke_large_request(config, 1000000 + 4, 2000000);
+  test_invoke_large_request(config, 1000000 + 5, 2000000);
+  test_invoke_large_request(config, 1000000 + 6, 2000000);
 }
 
 void invoke_large_request_pre_init(void) {}
diff --git a/test/core/end2end/tests/large_metadata.c b/test/core/end2end/tests/large_metadata.c
index 1f27896..eb174a2 100644
--- a/test/core/end2end/tests/large_metadata.c
+++ b/test/core/end2end/tests/large_metadata.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -181,7 +179,7 @@
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -200,7 +198,7 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -222,8 +220,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(103), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
diff --git a/test/core/end2end/tests/load_reporting_hook.c b/test/core/end2end/tests/load_reporting_hook.c
new file mode 100644
index 0000000..59d054c
--- /dev/null
+++ b/test/core/end2end/tests/load_reporting_hook.c
@@ -0,0 +1,321 @@
+/*
+ *
+ * Copyright 2016, 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 <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+#include "src/core/ext/load_reporting/load_reporting.h"
+#include "src/core/ext/load_reporting/load_reporting_filter.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(intptr_t t) { return (void *)t; }
+
+typedef struct {
+  gpr_mu mu;
+  intptr_t channel_id;
+  intptr_t call_id;
+
+  char *initial_md_str;
+  char *trailing_md_str;
+  char *method_name;
+
+  uint64_t incoming_bytes;
+  uint64_t outgoing_bytes;
+
+  grpc_status_code call_final_status;
+
+  bool fully_processed;
+} load_reporting_data;
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char *test_name,
+                                            grpc_channel_args *client_args,
+                                            grpc_channel_args *server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+
+  return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+  return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(n);
+}
+
+static gpr_timespec five_seconds_time(void) { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_time(), NULL);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(
+                 f->cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+}
+
+static void request_response_with_payload(grpc_end2end_test_fixture f,
+                                          const char *method_name,
+                                          const char *request_msg,
+                                          const char *response_msg,
+                                          grpc_metadata *initial_lr_metadata,
+                                          grpc_metadata *trailing_lr_metadata) {
+  gpr_slice request_payload_slice = gpr_slice_from_static_string(request_msg);
+  gpr_slice response_payload_slice = gpr_slice_from_static_string(response_msg);
+  grpc_call *c;
+  grpc_call *s;
+  grpc_byte_buffer *request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer *response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  gpr_timespec deadline = five_seconds_time();
+  cq_verifier *cqv = cq_verifier_create(f.cq);
+  grpc_op ops[6];
+  grpc_op *op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_byte_buffer *request_payload_recv = NULL;
+  grpc_byte_buffer *response_payload_recv = NULL;
+  grpc_call_details call_details;
+  grpc_status_code status;
+  grpc_call_error error;
+  char *details = NULL;
+  size_t details_capacity = 0;
+  int was_cancelled = 2;
+
+  c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+                               method_name, "foo.test.google.fr", deadline,
+                               NULL);
+  GPR_ASSERT(c);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  GPR_ASSERT(initial_lr_metadata != NULL);
+  op->data.send_initial_metadata.count = 1;
+  op->data.send_initial_metadata.metadata = initial_lr_metadata;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message = request_payload;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message = &response_payload_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
+  cq_verify(cqv);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message = &request_payload_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  cq_verify(cqv);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message = response_payload;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  GPR_ASSERT(trailing_lr_metadata != NULL);
+  op->data.send_status_from_server.trailing_metadata_count = 1;
+  op->data.send_status_from_server.trailing_metadata = trailing_lr_metadata;
+  op->data.send_status_from_server.status = GRPC_STATUS_OK;
+  op->data.send_status_from_server.status_details = "xyz";
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+
+  gpr_free(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+
+  grpc_call_destroy(c);
+  grpc_call_destroy(s);
+
+  cq_verifier_destroy(cqv);
+
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+}
+
+/* override the default for testing purposes */
+extern void (*g_load_reporting_fn)(
+    const grpc_load_reporting_call_data *call_data);
+
+static void test_load_reporting_hook(grpc_end2end_test_config config) {
+  /* TODO(dgq): this test is currently a noop until LR is fully defined.
+   * Leaving the rest here, as it'll likely be reusable. */
+
+  /* Introduce load reporting for the server through its arguments */
+  grpc_arg arg = grpc_load_reporting_enable_arg();
+  grpc_channel_args *lr_server_args =
+      grpc_channel_args_copy_and_add(NULL, &arg, 1);
+
+  grpc_end2end_test_fixture f =
+      begin_test(config, "test_load_reporting_hook", NULL, lr_server_args);
+
+  const char *method_name = "/gRPCFTW";
+  const char *request_msg = "the msg from the client";
+  const char *response_msg = "... and the response from the server";
+
+  grpc_metadata initial_lr_metadata;
+  grpc_metadata trailing_lr_metadata;
+
+  initial_lr_metadata.key = GRPC_LOAD_REPORTING_INITIAL_MD_KEY;
+  initial_lr_metadata.value = "client-token";
+  initial_lr_metadata.value_length = strlen(initial_lr_metadata.value);
+  memset(&initial_lr_metadata.internal_data, 0,
+         sizeof(initial_lr_metadata.internal_data));
+
+  trailing_lr_metadata.key = GRPC_LOAD_REPORTING_TRAILING_MD_KEY;
+  trailing_lr_metadata.value = "server-token";
+  trailing_lr_metadata.value_length = strlen(trailing_lr_metadata.value);
+  memset(&trailing_lr_metadata.internal_data, 0,
+         sizeof(trailing_lr_metadata.internal_data));
+
+  request_response_with_payload(f, method_name, request_msg, response_msg,
+                                &initial_lr_metadata, &trailing_lr_metadata);
+  end_test(&f);
+  grpc_channel_args_destroy(lr_server_args);
+  config.tear_down_data(&f);
+}
+
+void load_reporting_hook(grpc_end2end_test_config config) {
+  test_load_reporting_hook(config);
+}
+
+void load_reporting_hook_pre_init(void) {}
diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c
index 41de74f..65fa946 100644
--- a/test/core/end2end/tests/max_concurrent_streams.c
+++ b/test/core/end2end/tests/max_concurrent_streams.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -155,7 +153,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -180,8 +178,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
@@ -382,17 +380,17 @@
   error = grpc_call_start_batch(s1, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(live_call + 2), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(live_call + 2), 1);
   /* first request is finished, we should be able to start the second */
   live_call = (live_call == 300) ? 400 : 300;
-  cq_expect_completion(cqv, tag(live_call + 1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(live_call + 1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(
                                  f.server, &s2, &call_details,
                                  &request_metadata_recv, f.cq, f.cq, tag(201)));
-  cq_expect_completion(cqv, tag(201), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(201), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -417,8 +415,8 @@
   error = grpc_call_start_batch(s2, ops, (size_t)(op - ops), tag(202), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(live_call + 2), 1);
-  cq_expect_completion(cqv, tag(202), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(live_call + 2), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(202), 1);
   cq_verify(cqv);
 
   cq_verifier_destroy(cqv);
diff --git a/test/core/end2end/tests/max_message_length.c b/test/core/end2end/tests/max_message_length.c
index 08d326a..cdca3e6 100644
--- a/test/core/end2end/tests/max_message_length.c
+++ b/test/core/end2end/tests/max_message_length.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -100,19 +98,22 @@
   grpc_completion_queue_destroy(f->cq);
 }
 
-static void test_max_message_length(grpc_end2end_test_config config) {
+static void test_max_message_length(grpc_end2end_test_config config,
+                                    bool send_limit) {
+  gpr_log(GPR_INFO, "testing with send_limit=%d", send_limit);
+
   grpc_end2end_test_fixture f;
-  grpc_arg server_arg;
-  grpc_channel_args server_args;
-  grpc_call *c;
-  grpc_call *s;
+  grpc_arg channel_arg;
+  grpc_channel_args channel_args;
+  grpc_call *c = NULL;
+  grpc_call *s = NULL;
   cq_verifier *cqv;
   grpc_op ops[6];
   grpc_op *op;
   gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
   grpc_byte_buffer *request_payload =
       grpc_raw_byte_buffer_create(&request_payload_slice, 1);
-  grpc_byte_buffer *recv_payload;
+  grpc_byte_buffer *recv_payload = NULL;
   grpc_metadata_array initial_metadata_recv;
   grpc_metadata_array trailing_metadata_recv;
   grpc_metadata_array request_metadata_recv;
@@ -123,14 +124,17 @@
   size_t details_capacity = 0;
   int was_cancelled = 2;
 
-  server_arg.key = GRPC_ARG_MAX_MESSAGE_LENGTH;
-  server_arg.type = GRPC_ARG_INTEGER;
-  server_arg.value.integer = 5;
+  channel_arg.key = send_limit ? GRPC_ARG_MAX_SEND_MESSAGE_LENGTH
+                               : GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH;
+  channel_arg.type = GRPC_ARG_INTEGER;
+  channel_arg.value.integer = 5;
 
-  server_args.num_args = 1;
-  server_args.args = &server_arg;
+  channel_args.num_args = 1;
+  channel_args.args = &channel_arg;
 
-  f = begin_test(config, "test_max_message_length", NULL, &server_args);
+  f = begin_test(config, "test_max_message_length",
+                 send_limit ? &channel_args : NULL,
+                 send_limit ? NULL : &channel_args);
   cqv = cq_verifier_create(f.cq);
 
   c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
@@ -175,11 +179,17 @@
   error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
+  if (send_limit) {
+    CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+    cq_verify(cqv);
+    goto done;
+  }
+
   error =
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -197,15 +207,20 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
-  GPR_ASSERT(status != GRPC_STATUS_OK);
   GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
   GPR_ASSERT(0 == strcmp(call_details.host, "foo.test.google.fr:1234"));
   GPR_ASSERT(was_cancelled == 1);
-  GPR_ASSERT(recv_payload == NULL);
+
+done:
+  GPR_ASSERT(status == GRPC_STATUS_INVALID_ARGUMENT);
+  GPR_ASSERT(strcmp(details,
+                    send_limit
+                        ? "Sent message larger than max (11 vs. 5)"
+                        : "Received message larger than max (11 vs. 5)") == 0);
 
   gpr_free(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
@@ -213,9 +228,10 @@
   grpc_metadata_array_destroy(&request_metadata_recv);
   grpc_call_details_destroy(&call_details);
   grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(recv_payload);
 
   grpc_call_destroy(c);
-  grpc_call_destroy(s);
+  if (s != NULL) grpc_call_destroy(s);
 
   cq_verifier_destroy(cqv);
 
@@ -224,7 +240,8 @@
 }
 
 void max_message_length(grpc_end2end_test_config config) {
-  test_max_message_length(config);
+  test_max_message_length(config, true);
+  test_max_message_length(config, false);
 }
 
 void max_message_length_pre_init(void) {}
diff --git a/test/core/end2end/tests/negative_deadline.c b/test/core/end2end/tests/negative_deadline.c
index dff7992..c999ac1 100644
--- a/test/core/end2end/tests/negative_deadline.c
+++ b/test/core/end2end/tests/negative_deadline.c
@@ -45,8 +45,6 @@
 #include "src/core/lib/support/string.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -150,7 +148,7 @@
   error = grpc_call_start_batch(c, ops, num_ops, tag(1), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_DEADLINE_EXCEEDED);
diff --git a/test/core/end2end/tests/network_status_change.c b/test/core/end2end/tests/network_status_change.c
index 39ddc13..1d4b6db 100644
--- a/test/core/end2end/tests/network_status_change.c
+++ b/test/core/end2end/tests/network_status_change.c
@@ -46,8 +46,6 @@
 /* this is a private API but exposed here for testing*/
 extern void grpc_network_status_shutdown_all_endpoints();
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -168,7 +166,7 @@
   GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(
                                  f.server, &s, &call_details,
                                  &request_metadata_recv, f.cq, f.cq, tag(101)));
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   op = ops;
@@ -185,7 +183,7 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
   cq_verify(cqv);
 
   // Simulate the network loss event
@@ -207,8 +205,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(103), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   // Expected behavior of a RPC when network is lost.
diff --git a/test/core/end2end/tests/no_logging.c b/test/core/end2end/tests/no_logging.c
new file mode 100644
index 0000000..430bfdc
--- /dev/null
+++ b/test/core/end2end/tests/no_logging.c
@@ -0,0 +1,302 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/support/string.h"
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(intptr_t t) { return (void *)t; }
+
+extern void gpr_default_log(gpr_log_func_args *args);
+
+static void test_no_log(gpr_log_func_args *args) {
+  char *message = NULL;
+  gpr_asprintf(&message, "Unwanted log: %s", args->message);
+  args->message = message;
+  gpr_default_log(args);
+  gpr_free(message);
+  abort();
+}
+
+static void test_no_error_log(gpr_log_func_args *args) {
+  if (args->severity == GPR_LOG_SEVERITY_ERROR) {
+    test_no_log(args);
+  }
+}
+
+static gpr_atm g_log_func = (gpr_atm)gpr_default_log;
+
+static void log_dispatcher_func(gpr_log_func_args *args) {
+  gpr_log_func log_func = (gpr_log_func)gpr_atm_no_barrier_load(&g_log_func);
+  log_func(args);
+}
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char *test_name,
+                                            grpc_channel_args *client_args,
+                                            grpc_channel_args *server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+  return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(n);
+}
+
+static gpr_timespec five_seconds_time(void) { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_time(), NULL);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(
+                 f->cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+}
+
+static void simple_request_body(grpc_end2end_test_fixture f) {
+  grpc_call *c;
+  grpc_call *s;
+  gpr_timespec deadline = five_seconds_time();
+  cq_verifier *cqv = cq_verifier_create(f.cq);
+  grpc_op ops[6];
+  grpc_op *op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_status_code status;
+  grpc_call_error error;
+  char *details = NULL;
+  size_t details_capacity = 0;
+  int was_cancelled = 2;
+  char *peer;
+
+  c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+                               "/foo", "foo.test.google.fr:1234", deadline,
+                               NULL);
+  GPR_ASSERT(c);
+
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != NULL);
+  gpr_free(peer);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
+  cq_verify(cqv);
+
+  peer = grpc_call_get_peer(s);
+  GPR_ASSERT(peer != NULL);
+  gpr_free(peer);
+  peer = grpc_call_get_peer(c);
+  GPR_ASSERT(peer != NULL);
+  gpr_free(peer);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
+  op->data.send_status_from_server.status_details = "xyz";
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
+  GPR_ASSERT(0 == strcmp(details, "xyz"));
+  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == strcmp(call_details.host, "foo.test.google.fr:1234"));
+  GPR_ASSERT(0 == call_details.flags);
+  GPR_ASSERT(was_cancelled == 1);
+
+  gpr_free(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+
+  grpc_call_destroy(c);
+  grpc_call_destroy(s);
+
+  cq_verifier_destroy(cqv);
+}
+
+static void test_invoke_simple_request(grpc_end2end_test_config config) {
+  grpc_end2end_test_fixture f;
+
+  f = begin_test(config, "test_invoke_simple_request_with_no_error_logging",
+                 NULL, NULL);
+  simple_request_body(f);
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+static void test_invoke_10_simple_requests(grpc_end2end_test_config config) {
+  int i;
+  grpc_end2end_test_fixture f =
+      begin_test(config, "test_invoke_10_simple_requests_with_no_error_logging",
+                 NULL, NULL);
+  for (i = 0; i < 10; i++) {
+    simple_request_body(f);
+    gpr_log(GPR_INFO, "Passed simple request %d", i);
+  }
+  simple_request_body(f);
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+static void test_no_error_logging_in_entire_process(
+    grpc_end2end_test_config config) {
+  int i;
+  gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)test_no_error_log);
+  for (i = 0; i < 10; i++) {
+    test_invoke_simple_request(config);
+  }
+  test_invoke_10_simple_requests(config);
+  gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)gpr_default_log);
+}
+
+static void test_no_logging_in_one_request(grpc_end2end_test_config config) {
+  int i;
+  grpc_end2end_test_fixture f =
+      begin_test(config, "test_no_logging_in_last_request", NULL, NULL);
+  for (i = 0; i < 10; i++) {
+    simple_request_body(f);
+  }
+  gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)test_no_log);
+  simple_request_body(f);
+  gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)gpr_default_log);
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void no_logging(grpc_end2end_test_config config) {
+  gpr_set_log_function(log_dispatcher_func);
+  test_no_logging_in_one_request(config);
+  test_no_error_logging_in_entire_process(config);
+  gpr_set_log_function(gpr_default_log);
+}
+
+void no_logging_pre_init(void) {}
diff --git a/test/core/end2end/tests/no_op.c b/test/core/end2end/tests/no_op.c
index 284be7a..8b29c21 100644
--- a/test/core/end2end/tests/no_op.c
+++ b/test/core/end2end/tests/no_op.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
diff --git a/test/core/end2end/tests/payload.c b/test/core/end2end/tests/payload.c
index 443d85e..c71704f 100644
--- a/test/core/end2end/tests/payload.c
+++ b/test/core/end2end/tests/payload.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -172,7 +170,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -190,7 +188,7 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -215,8 +213,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(103), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
diff --git a/test/core/end2end/tests/ping.c b/test/core/end2end/tests/ping.c
index d49bec9..5e5169d 100644
--- a/test/core/end2end/tests/ping.c
+++ b/test/core/end2end/tests/ping.c
@@ -52,7 +52,7 @@
   config.init_server(&f, NULL);
 
   grpc_channel_ping(f.client, f.cq, tag(0), NULL);
-  cq_expect_completion(cqv, tag(0), 0);
+  CQ_EXPECT_COMPLETION(cqv, tag(0), 0);
 
   /* check that we're still in idle, and start connecting */
   GPR_ASSERT(grpc_channel_check_connectivity_state(f.client, 1) ==
@@ -62,7 +62,7 @@
   while (state != GRPC_CHANNEL_READY) {
     grpc_channel_watch_connectivity_state(
         f.client, state, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3), f.cq, tag(99));
-    cq_expect_completion(cqv, tag(99), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(99), 1);
     cq_verify(cqv);
     state = grpc_channel_check_connectivity_state(f.client, 0);
     GPR_ASSERT(state == GRPC_CHANNEL_READY ||
@@ -72,12 +72,12 @@
 
   for (i = 1; i <= 5; i++) {
     grpc_channel_ping(f.client, f.cq, tag(i), NULL);
-    cq_expect_completion(cqv, tag(i), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(i), 1);
     cq_verify(cqv);
   }
 
   grpc_server_shutdown_and_notify(f.server, f.cq, tag(0xdead));
-  cq_expect_completion(cqv, tag(0xdead), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(0xdead), 1);
   cq_verify(cqv);
 
   /* cleanup server */
diff --git a/test/core/end2end/tests/ping_pong_streaming.c b/test/core/end2end/tests/ping_pong_streaming.c
index 1d2f794..7e360c4 100644
--- a/test/core/end2end/tests/ping_pong_streaming.c
+++ b/test/core/end2end/tests/ping_pong_streaming.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -162,7 +160,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(100));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(100), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(100), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -208,7 +206,7 @@
     op++;
     error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
     GPR_ASSERT(GRPC_CALL_OK == error);
-    cq_expect_completion(cqv, tag(102), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
     cq_verify(cqv);
 
     memset(ops, 0, sizeof(ops));
@@ -220,8 +218,8 @@
     op++;
     error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
     GPR_ASSERT(GRPC_CALL_OK == error);
-    cq_expect_completion(cqv, tag(103), 1);
-    cq_expect_completion(cqv, tag(2), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(2), 1);
     cq_verify(cqv);
 
     grpc_byte_buffer_destroy(request_payload);
@@ -254,10 +252,10 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(104), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(1), 1);
-  cq_expect_completion(cqv, tag(3), 1);
-  cq_expect_completion(cqv, tag(101), 1);
-  cq_expect_completion(cqv, tag(104), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(3), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(104), 1);
   cq_verify(cqv);
 
   grpc_call_destroy(c);
diff --git a/test/core/end2end/tests/registered_call.c b/test/core/end2end/tests/registered_call.c
index ece6250..9b2b42b 100644
--- a/test/core/end2end/tests/registered_call.c
+++ b/test/core/end2end/tests/registered_call.c
@@ -45,8 +45,6 @@
 #include "src/core/lib/support/string.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -156,7 +154,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -181,8 +179,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
diff --git a/test/core/end2end/tests/request_with_flags.c b/test/core/end2end/tests/request_with_flags.c
index b5d398b..25150e6 100644
--- a/test/core/end2end/tests/request_with_flags.c
+++ b/test/core/end2end/tests/request_with_flags.c
@@ -44,8 +44,6 @@
 #include "src/core/lib/transport/byte_stream.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -165,7 +163,7 @@
   GPR_ASSERT(expectation == error);
 
   if (expectation == GRPC_CALL_OK) {
-    cq_expect_completion(cqv, tag(1), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
     cq_verify(cqv);
   }
 
diff --git a/test/core/end2end/tests/request_with_payload.c b/test/core/end2end/tests/request_with_payload.c
index d94267e..67208c0 100644
--- a/test/core/end2end/tests/request_with_payload.c
+++ b/test/core/end2end/tests/request_with_payload.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -165,7 +163,7 @@
   GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(
                                  f.server, &s, &call_details,
                                  &request_metadata_recv, f.cq, f.cq, tag(101)));
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -183,7 +181,7 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -203,8 +201,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(103), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
diff --git a/test/core/end2end/tests/server_finishes_request.c b/test/core/end2end/tests/server_finishes_request.c
index a723c6f..7fb9025 100644
--- a/test/core/end2end/tests/server_finishes_request.c
+++ b/test/core/end2end/tests/server_finishes_request.c
@@ -45,8 +45,6 @@
 #include "src/core/lib/support/string.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -153,7 +151,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -178,8 +176,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
diff --git a/test/core/end2end/tests/shutdown_finishes_calls.c b/test/core/end2end/tests/shutdown_finishes_calls.c
index abb6b26..dff2e6f 100644
--- a/test/core/end2end/tests/shutdown_finishes_calls.c
+++ b/test/core/end2end/tests/shutdown_finishes_calls.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -147,7 +145,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -164,9 +162,9 @@
   grpc_server_shutdown_and_notify(f.server, f.cq, tag(1000));
   grpc_server_cancel_all_calls(f.server);
 
-  cq_expect_completion(cqv, tag(1000), 1);
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1000), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   grpc_server_destroy(f.server);
diff --git a/test/core/end2end/tests/shutdown_finishes_tags.c b/test/core/end2end/tests/shutdown_finishes_tags.c
index b1f3c94..1d110a7 100644
--- a/test/core/end2end/tests/shutdown_finishes_tags.c
+++ b/test/core/end2end/tests/shutdown_finishes_tags.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -104,8 +102,8 @@
                                  f.server, &s, &call_details,
                                  &request_metadata_recv, f.cq, f.cq, tag(101)));
   grpc_server_shutdown_and_notify(f.server, f.cq, tag(1000));
-  cq_expect_completion(cqv, tag(101), 0);
-  cq_expect_completion(cqv, tag(1000), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 0);
+  CQ_EXPECT_COMPLETION(cqv, tag(1000), 1);
   cq_verify(cqv);
   GPR_ASSERT(s == NULL);
 
diff --git a/test/core/end2end/tests/simple_cacheable_request.c b/test/core/end2end/tests/simple_cacheable_request.c
new file mode 100644
index 0000000..b52eb19
--- /dev/null
+++ b/test/core/end2end/tests/simple_cacheable_request.c
@@ -0,0 +1,276 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(intptr_t t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+                                            const char *test_name,
+                                            grpc_channel_args *client_args,
+                                            grpc_channel_args *server_args) {
+  grpc_end2end_test_fixture f;
+  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  f = config.create_fixture(client_args, server_args);
+  config.init_server(&f, server_args);
+  config.init_client(&f, client_args);
+  return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+  return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(n);
+}
+
+static gpr_timespec five_seconds_time(void) { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, five_seconds_time(), NULL);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+  if (!f->server) return;
+  grpc_server_shutdown_and_notify(f->server, f->cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(
+                 f->cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(f->server);
+  f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+  if (!f->client) return;
+  grpc_channel_destroy(f->client);
+  f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+  shutdown_server(f);
+  shutdown_client(f);
+
+  grpc_completion_queue_shutdown(f->cq);
+  drain_cq(f->cq);
+  grpc_completion_queue_destroy(f->cq);
+}
+
+/* Request/response with metadata and payload.*/
+static void test_cacheable_request_response_with_metadata_and_payload(
+    grpc_end2end_test_config config) {
+  grpc_call *c;
+  grpc_call *s;
+  gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
+  gpr_slice response_payload_slice = gpr_slice_from_copied_string("hello you");
+  grpc_byte_buffer *request_payload =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_byte_buffer *response_payload =
+      grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+  gpr_timespec deadline = five_seconds_time();
+  grpc_metadata meta_c[2] = {
+      {"key1", "val1", 4, 0, {{NULL, NULL, NULL, NULL}}},
+      {"key2", "val2", 4, 0, {{NULL, NULL, NULL, NULL}}}};
+  grpc_metadata meta_s[2] = {
+      {"key3", "val3", 4, 0, {{NULL, NULL, NULL, NULL}}},
+      {"key4", "val4", 4, 0, {{NULL, NULL, NULL, NULL}}}};
+  grpc_end2end_test_fixture f = begin_test(
+      config, "test_cacheable_request_response_with_metadata_and_payload", NULL,
+      NULL);
+  cq_verifier *cqv = cq_verifier_create(f.cq);
+  grpc_op ops[6];
+  grpc_op *op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_metadata_array request_metadata_recv;
+  grpc_byte_buffer *request_payload_recv = NULL;
+  grpc_byte_buffer *response_payload_recv = NULL;
+  grpc_call_details call_details;
+  grpc_status_code status;
+  grpc_call_error error;
+  char *details = NULL;
+  size_t details_capacity = 0;
+  int was_cancelled = 2;
+
+  c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+                               "/foo", "foo.test.google.fr", deadline, NULL);
+  GPR_ASSERT(c);
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 2;
+  op->data.send_initial_metadata.metadata = meta_c;
+  op->flags = GRPC_INITIAL_METADATA_CACHEABLE_REQUEST;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message = request_payload;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message = &response_payload_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
+  cq_verify(cqv);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 2;
+  op->data.send_initial_metadata.metadata = meta_s;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message = &request_payload_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  cq_verify(cqv);
+
+  memset(ops, 0, sizeof(ops));
+  op = ops;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message = response_payload;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_OK;
+  op->data.send_status_from_server.status_details = "xyz";
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+  cq_verify(cqv);
+
+  GPR_ASSERT(status == GRPC_STATUS_OK);
+  GPR_ASSERT(0 == strcmp(details, "xyz"));
+  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == strcmp(call_details.host, "foo.test.google.fr"));
+  if (config.feature_mask & FEATURE_MASK_SUPPORTS_REQUEST_PROXYING) {
+    // Our simple proxy does not support cacheable requests
+  } else {
+    GPR_ASSERT(GRPC_INITIAL_METADATA_CACHEABLE_REQUEST & call_details.flags);
+  }
+  GPR_ASSERT(was_cancelled == 0);
+  GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world"));
+  GPR_ASSERT(byte_buffer_eq_string(response_payload_recv, "hello you"));
+  GPR_ASSERT(contains_metadata(&request_metadata_recv, "key1", "val1"));
+  GPR_ASSERT(contains_metadata(&request_metadata_recv, "key2", "val2"));
+  GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key3", "val3"));
+  GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key4", "val4"));
+
+  gpr_free(details);
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+
+  grpc_call_destroy(c);
+  grpc_call_destroy(s);
+
+  cq_verifier_destroy(cqv);
+
+  grpc_byte_buffer_destroy(request_payload);
+  grpc_byte_buffer_destroy(response_payload);
+  grpc_byte_buffer_destroy(request_payload_recv);
+  grpc_byte_buffer_destroy(response_payload_recv);
+
+  end_test(&f);
+  config.tear_down_data(&f);
+}
+
+void simple_cacheable_request(grpc_end2end_test_config config) {
+  test_cacheable_request_response_with_metadata_and_payload(config);
+}
+
+void simple_cacheable_request_pre_init(void) {}
diff --git a/test/core/end2end/tests/simple_delayed_request.c b/test/core/end2end/tests/simple_delayed_request.c
index e1fcc03..74f1232 100644
--- a/test/core/end2end/tests/simple_delayed_request.c
+++ b/test/core/end2end/tests/simple_delayed_request.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static gpr_timespec n_seconds_time(int n) {
@@ -150,7 +148,7 @@
       grpc_server_request_call(f->server, &s, &call_details,
                                &request_metadata_recv, f->cq, f->cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -175,8 +173,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
diff --git a/test/core/end2end/tests/simple_metadata.c b/test/core/end2end/tests/simple_metadata.c
index c9b1a03..13c77c0 100644
--- a/test/core/end2end/tests/simple_metadata.c
+++ b/test/core/end2end/tests/simple_metadata.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -183,7 +181,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -202,7 +200,7 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -227,8 +225,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(103), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
diff --git a/test/core/end2end/tests/simple_request.c b/test/core/end2end/tests/simple_request.c
index a8014e6..ed7b850 100644
--- a/test/core/end2end/tests/simple_request.c
+++ b/test/core/end2end/tests/simple_request.c
@@ -45,8 +45,6 @@
 #include "src/core/lib/support/string.h"
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -163,7 +161,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   peer = grpc_call_get_peer(s);
@@ -197,8 +195,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
diff --git a/test/core/end2end/tests/streaming_error_response.c b/test/core/end2end/tests/streaming_error_response.c
index e15c132..8285468 100644
--- a/test/core/end2end/tests/streaming_error_response.c
+++ b/test/core/end2end/tests/streaming_error_response.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -163,7 +161,7 @@
   GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(
                                  f.server, &s, &call_details,
                                  &request_metadata_recv, f.cq, f.cq, tag(101)));
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -177,7 +175,7 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -188,9 +186,9 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(103), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
   if (!request_status_early) {
-    cq_expect_completion(cqv, tag(1), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   }
   cq_verify(cqv);
 
@@ -217,11 +215,11 @@
     GPR_ASSERT(GRPC_CALL_OK == error);
   }
 
-  cq_expect_completion(cqv, tag(104), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(104), 1);
   if (request_status_early) {
-    cq_expect_completion(cqv, tag(1), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   } else {
-    cq_expect_completion(cqv, tag(2), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(2), 1);
   }
   cq_verify(cqv);
 
@@ -237,7 +235,7 @@
     error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(3), NULL);
     GPR_ASSERT(GRPC_CALL_OK == error);
 
-    cq_expect_completion(cqv, tag(3), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(3), 1);
     cq_verify(cqv);
 
     GPR_ASSERT(response_payload1_recv != NULL);
diff --git a/test/core/end2end/tests/trailing_metadata.c b/test/core/end2end/tests/trailing_metadata.c
index 41e0f00..d7093ae 100644
--- a/test/core/end2end/tests/trailing_metadata.c
+++ b/test/core/end2end/tests/trailing_metadata.c
@@ -43,8 +43,6 @@
 #include <grpc/support/useful.h>
 #include "test/core/end2end/cq_verifier.h"
 
-enum { TIMEOUT = 200000 };
-
 static void *tag(intptr_t t) { return (void *)t; }
 
 static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
@@ -186,7 +184,7 @@
       grpc_server_request_call(f.server, &s, &call_details,
                                &request_metadata_recv, f.cq, f.cq, tag(101));
   GPR_ASSERT(GRPC_CALL_OK == error);
-  cq_expect_completion(cqv, tag(101), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -205,7 +203,7 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(102), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -231,8 +229,8 @@
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  cq_expect_completion(cqv, tag(103), 1);
-  cq_expect_completion(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(103), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
diff --git a/test/core/http/parser_test.c b/test/core/http/parser_test.c
index d645d28..2fc354d 100644
--- a/test/core/http/parser_test.c
+++ b/test/core/http/parser_test.c
@@ -62,7 +62,8 @@
   grpc_http_parser_init(&parser, GRPC_HTTP_REQUEST, &request);
 
   for (i = 0; i < num_slices; i++) {
-    GPR_ASSERT(grpc_http_parser_parse(&parser, slices[i]) == GRPC_ERROR_NONE);
+    GPR_ASSERT(grpc_http_parser_parse(&parser, slices[i], NULL) ==
+               GRPC_ERROR_NONE);
     gpr_slice_unref(slices[i]);
   }
   GPR_ASSERT(grpc_http_parser_eof(&parser) == GRPC_ERROR_NONE);
@@ -118,7 +119,8 @@
   grpc_http_parser_init(&parser, GRPC_HTTP_RESPONSE, &response);
 
   for (i = 0; i < num_slices; i++) {
-    GPR_ASSERT(grpc_http_parser_parse(&parser, slices[i]) == GRPC_ERROR_NONE);
+    GPR_ASSERT(grpc_http_parser_parse(&parser, slices[i], NULL) ==
+               GRPC_ERROR_NONE);
     gpr_slice_unref(slices[i]);
   }
   GPR_ASSERT(grpc_http_parser_eof(&parser) == GRPC_ERROR_NONE);
@@ -171,7 +173,7 @@
 
   for (i = 0; i < num_slices; i++) {
     if (GRPC_ERROR_NONE == error) {
-      error = grpc_http_parser_parse(&parser, slices[i]);
+      error = grpc_http_parser_parse(&parser, slices[i], NULL);
     }
     gpr_slice_unref(slices[i]);
   }
@@ -204,7 +206,7 @@
 
   for (i = 0; i < num_slices; i++) {
     if (error == GRPC_ERROR_NONE) {
-      error = grpc_http_parser_parse(&parser, slices[i]);
+      error = grpc_http_parser_parse(&parser, slices[i], NULL);
     }
     gpr_slice_unref(slices[i]);
   }
diff --git a/test/core/http/request_fuzzer.c b/test/core/http/request_fuzzer.c
index 5941401..bb6cb92 100644
--- a/test/core/http/request_fuzzer.c
+++ b/test/core/http/request_fuzzer.c
@@ -48,7 +48,7 @@
   memset(&request, 0, sizeof(request));
   grpc_http_parser_init(&parser, GRPC_HTTP_REQUEST, &request);
   gpr_slice slice = gpr_slice_from_copied_buffer((const char *)data, size);
-  GRPC_ERROR_UNREF(grpc_http_parser_parse(&parser, slice));
+  GRPC_ERROR_UNREF(grpc_http_parser_parse(&parser, slice, NULL));
   GRPC_ERROR_UNREF(grpc_http_parser_eof(&parser));
   gpr_slice_unref(slice);
   grpc_http_parser_destroy(&parser);
diff --git a/test/core/http/response_fuzzer.c b/test/core/http/response_fuzzer.c
index acde7c8..4393840 100644
--- a/test/core/http/response_fuzzer.c
+++ b/test/core/http/response_fuzzer.c
@@ -47,7 +47,7 @@
   memset(&response, 0, sizeof(response));
   grpc_http_parser_init(&parser, GRPC_HTTP_RESPONSE, &response);
   gpr_slice slice = gpr_slice_from_copied_buffer((const char *)data, size);
-  GRPC_ERROR_UNREF(grpc_http_parser_parse(&parser, slice));
+  GRPC_ERROR_UNREF(grpc_http_parser_parse(&parser, slice, NULL));
   GRPC_ERROR_UNREF(grpc_http_parser_eof(&parser));
   gpr_slice_unref(slice);
   grpc_http_parser_destroy(&parser);
diff --git a/test/core/iomgr/combiner_test.c b/test/core/iomgr/combiner_test.c
new file mode 100644
index 0000000..197998c
--- /dev/null
+++ b/test/core/iomgr/combiner_test.c
@@ -0,0 +1,164 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/iomgr/combiner.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/useful.h>
+
+#include "test/core/util/test_config.h"
+
+static void test_no_op(void) {
+  gpr_log(GPR_DEBUG, "test_no_op");
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_combiner_destroy(&exec_ctx, grpc_combiner_create(NULL));
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void set_bool_to_true(grpc_exec_ctx *exec_ctx, void *value,
+                             grpc_error *error) {
+  *(bool *)value = true;
+}
+
+static void test_execute_one(void) {
+  gpr_log(GPR_DEBUG, "test_execute_one");
+
+  grpc_combiner *lock = grpc_combiner_create(NULL);
+  bool done = false;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_combiner_execute(&exec_ctx, lock,
+                        grpc_closure_create(set_bool_to_true, &done),
+                        GRPC_ERROR_NONE);
+  grpc_exec_ctx_flush(&exec_ctx);
+  GPR_ASSERT(done);
+  grpc_combiner_destroy(&exec_ctx, lock);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+typedef struct {
+  size_t ctr;
+  grpc_combiner *lock;
+} thd_args;
+
+typedef struct {
+  size_t *ctr;
+  size_t value;
+} ex_args;
+
+static void check_one(grpc_exec_ctx *exec_ctx, void *a, grpc_error *error) {
+  ex_args *args = a;
+  GPR_ASSERT(*args->ctr == args->value - 1);
+  *args->ctr = args->value;
+  gpr_free(a);
+}
+
+static void execute_many_loop(void *a) {
+  thd_args *args = a;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  size_t n = 1;
+  for (size_t i = 0; i < 10; i++) {
+    for (size_t j = 0; j < 10000; j++) {
+      ex_args *c = gpr_malloc(sizeof(*c));
+      c->ctr = &args->ctr;
+      c->value = n++;
+      grpc_combiner_execute(&exec_ctx, args->lock,
+                            grpc_closure_create(check_one, c), GRPC_ERROR_NONE);
+      grpc_exec_ctx_flush(&exec_ctx);
+    }
+    // sleep for a little bit, to test a combiner draining and another thread
+    // picking it up
+    gpr_sleep_until(GRPC_TIMEOUT_MILLIS_TO_DEADLINE(100));
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void test_execute_many(void) {
+  gpr_log(GPR_DEBUG, "test_execute_many");
+
+  grpc_combiner *lock = grpc_combiner_create(NULL);
+  gpr_thd_id thds[100];
+  thd_args ta[GPR_ARRAY_SIZE(thds)];
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
+    gpr_thd_options options = gpr_thd_options_default();
+    gpr_thd_options_set_joinable(&options);
+    ta[i].ctr = 0;
+    ta[i].lock = lock;
+    GPR_ASSERT(gpr_thd_new(&thds[i], execute_many_loop, &ta[i], &options));
+  }
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
+    gpr_thd_join(thds[i]);
+  }
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_combiner_destroy(&exec_ctx, lock);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static bool got_in_finally = false;
+
+static void in_finally(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  got_in_finally = true;
+}
+
+static void add_finally(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  grpc_combiner_execute_finally(exec_ctx, arg,
+                                grpc_closure_create(in_finally, NULL),
+                                GRPC_ERROR_NONE, false);
+}
+
+static void test_execute_finally(void) {
+  gpr_log(GPR_DEBUG, "test_execute_finally");
+
+  grpc_combiner *lock = grpc_combiner_create(NULL);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_combiner_execute(&exec_ctx, lock, grpc_closure_create(add_finally, lock),
+                        GRPC_ERROR_NONE);
+  grpc_exec_ctx_flush(&exec_ctx);
+  GPR_ASSERT(got_in_finally);
+  grpc_combiner_destroy(&exec_ctx, lock);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  grpc_init();
+  test_no_op();
+  test_execute_one();
+  test_execute_finally();
+  test_execute_many();
+  grpc_shutdown();
+
+  return 0;
+}
diff --git a/test/core/json/json_test.c b/test/core/json/json_test.c
index ac1abbd..7ea5cac 100644
--- a/test/core/json/json_test.c
+++ b/test/core/json/json_test.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/test/core/nanopb/fuzzer_response.c b/test/core/nanopb/fuzzer_response.c
index 21a5d7b..a82f20d 100644
--- a/test/core/nanopb/fuzzer_response.c
+++ b/test/core/nanopb/fuzzer_response.c
@@ -41,11 +41,14 @@
 bool squelch = true;
 bool leak_check = true;
 
+static void dont_log(gpr_log_func_args *args) {}
+
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  if (squelch) gpr_set_log_function(dont_log);
   gpr_slice slice = gpr_slice_from_copied_buffer((const char *)data, size);
-  grpc_grpclb_response *response;
-  if ((response = grpc_grpclb_response_parse(slice))) {
-    grpc_grpclb_response_destroy(response);
+  grpc_grpclb_initial_response *response;
+  if ((response = grpc_grpclb_initial_response_parse(slice))) {
+    grpc_grpclb_initial_response_destroy(response);
   }
   gpr_slice_unref(slice);
   return 0;
diff --git a/test/core/nanopb/fuzzer_serverlist.c b/test/core/nanopb/fuzzer_serverlist.c
index df2044d..9700bf1 100644
--- a/test/core/nanopb/fuzzer_serverlist.c
+++ b/test/core/nanopb/fuzzer_serverlist.c
@@ -41,7 +41,10 @@
 bool squelch = true;
 bool leak_check = true;
 
+static void dont_log(gpr_log_func_args *args) {}
+
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  if (squelch) gpr_set_log_function(dont_log);
   gpr_slice slice = gpr_slice_from_copied_buffer((const char *)data, size);
   grpc_grpclb_serverlist *serverlist;
   if ((serverlist = grpc_grpclb_response_parse_serverlist(slice))) {
diff --git a/test/core/support/mpscq_test.c b/test/core/support/mpscq_test.c
new file mode 100644
index 0000000..491eb91
--- /dev/null
+++ b/test/core/support/mpscq_test.c
@@ -0,0 +1,206 @@
+/*
+ *
+ * Copyright 2016, 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/lib/support/mpscq.h"
+
+#include <stdlib.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/test_config.h"
+
+typedef struct test_node {
+  gpr_mpscq_node node;
+  size_t i;
+  size_t *ctr;
+} test_node;
+
+static test_node *new_node(size_t i, size_t *ctr) {
+  test_node *n = gpr_malloc(sizeof(test_node));
+  n->i = i;
+  n->ctr = ctr;
+  return n;
+}
+
+static void test_serial(void) {
+  gpr_log(GPR_DEBUG, "test_serial");
+  gpr_mpscq q;
+  gpr_mpscq_init(&q);
+  for (size_t i = 0; i < 10000000; i++) {
+    gpr_mpscq_push(&q, &new_node(i, NULL)->node);
+  }
+  for (size_t i = 0; i < 10000000; i++) {
+    test_node *n = (test_node *)gpr_mpscq_pop(&q);
+    GPR_ASSERT(n);
+    GPR_ASSERT(n->i == i);
+    gpr_free(n);
+  }
+}
+
+typedef struct {
+  size_t ctr;
+  gpr_mpscq *q;
+  gpr_event *start;
+} thd_args;
+
+#define THREAD_ITERATIONS 100000
+
+static void test_thread(void *args) {
+  thd_args *a = args;
+  gpr_event_wait(a->start, gpr_inf_future(GPR_CLOCK_REALTIME));
+  for (size_t i = 1; i <= THREAD_ITERATIONS; i++) {
+    gpr_mpscq_push(a->q, &new_node(i, &a->ctr)->node);
+  }
+}
+
+static void test_mt(void) {
+  gpr_log(GPR_DEBUG, "test_mt");
+  gpr_event start;
+  gpr_event_init(&start);
+  gpr_thd_id thds[100];
+  thd_args ta[GPR_ARRAY_SIZE(thds)];
+  gpr_mpscq q;
+  gpr_mpscq_init(&q);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
+    gpr_thd_options options = gpr_thd_options_default();
+    gpr_thd_options_set_joinable(&options);
+    ta[i].ctr = 0;
+    ta[i].q = &q;
+    ta[i].start = &start;
+    GPR_ASSERT(gpr_thd_new(&thds[i], test_thread, &ta[i], &options));
+  }
+  size_t num_done = 0;
+  size_t spins = 0;
+  gpr_event_set(&start, (void *)1);
+  while (num_done != GPR_ARRAY_SIZE(thds)) {
+    gpr_mpscq_node *n;
+    while ((n = gpr_mpscq_pop(&q)) == NULL) {
+      spins++;
+    }
+    test_node *tn = (test_node *)n;
+    GPR_ASSERT(*tn->ctr == tn->i - 1);
+    *tn->ctr = tn->i;
+    if (tn->i == THREAD_ITERATIONS) num_done++;
+    gpr_free(tn);
+  }
+  gpr_log(GPR_DEBUG, "spins: %" PRIdPTR, spins);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
+    gpr_thd_join(thds[i]);
+  }
+  gpr_mpscq_destroy(&q);
+}
+
+typedef struct {
+  thd_args *ta;
+  size_t num_thds;
+  gpr_mu mu;
+  size_t num_done;
+  size_t spins;
+  gpr_mpscq *q;
+  gpr_event *start;
+} pull_args;
+
+static void pull_thread(void *arg) {
+  pull_args *pa = arg;
+  gpr_event_wait(pa->start, gpr_inf_future(GPR_CLOCK_REALTIME));
+
+  for (;;) {
+    gpr_mu_lock(&pa->mu);
+    if (pa->num_done == pa->num_thds) {
+      gpr_mu_unlock(&pa->mu);
+      return;
+    }
+    gpr_mpscq_node *n;
+    while ((n = gpr_mpscq_pop(pa->q)) == NULL) {
+      pa->spins++;
+    }
+    test_node *tn = (test_node *)n;
+    GPR_ASSERT(*tn->ctr == tn->i - 1);
+    *tn->ctr = tn->i;
+    if (tn->i == THREAD_ITERATIONS) pa->num_done++;
+    gpr_free(tn);
+    gpr_mu_unlock(&pa->mu);
+  }
+}
+
+static void test_mt_multipop(void) {
+  gpr_log(GPR_DEBUG, "test_mt_multipop");
+  gpr_event start;
+  gpr_event_init(&start);
+  gpr_thd_id thds[100];
+  gpr_thd_id pull_thds[100];
+  thd_args ta[GPR_ARRAY_SIZE(thds)];
+  gpr_mpscq q;
+  gpr_mpscq_init(&q);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
+    gpr_thd_options options = gpr_thd_options_default();
+    gpr_thd_options_set_joinable(&options);
+    ta[i].ctr = 0;
+    ta[i].q = &q;
+    ta[i].start = &start;
+    GPR_ASSERT(gpr_thd_new(&thds[i], test_thread, &ta[i], &options));
+  }
+  pull_args pa;
+  pa.ta = ta;
+  pa.num_thds = GPR_ARRAY_SIZE(thds);
+  pa.spins = 0;
+  pa.num_done = 0;
+  pa.q = &q;
+  pa.start = &start;
+  gpr_mu_init(&pa.mu);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(pull_thds); i++) {
+    gpr_thd_options options = gpr_thd_options_default();
+    gpr_thd_options_set_joinable(&options);
+    GPR_ASSERT(gpr_thd_new(&pull_thds[i], pull_thread, &pa, &options));
+  }
+  gpr_event_set(&start, (void *)1);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(pull_thds); i++) {
+    gpr_thd_join(pull_thds[i]);
+  }
+  gpr_log(GPR_DEBUG, "spins: %" PRIdPTR, pa.spins);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(thds); i++) {
+    gpr_thd_join(thds[i]);
+  }
+  gpr_mpscq_destroy(&q);
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  test_serial();
+  test_mt();
+  test_mt_multipop();
+  return 0;
+}
diff --git a/test/core/support/percent_decode_corpus/04cb8ccc553f9b2f5e52c421aff6d1c954d3dae6 b/test/core/support/percent_decode_corpus/04cb8ccc553f9b2f5e52c421aff6d1c954d3dae6
new file mode 100644
index 0000000..a0c7605
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/04cb8ccc553f9b2f5e52c421aff6d1c954d3dae6
@@ -0,0 +1 @@
+:Ê%cE'yzŠ
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/0dd8f3a63745b3a2d39791559b5c1b311447b537 b/test/core/support/percent_decode_corpus/0dd8f3a63745b3a2d39791559b5c1b311447b537
new file mode 100644
index 0000000..8b36124
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/0dd8f3a63745b3a2d39791559b5c1b311447b537
@@ -0,0 +1 @@
+x;x_%C88
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/17eeaca784409adbe43365c32ac87915d736bba3 b/test/core/support/percent_decode_corpus/17eeaca784409adbe43365c32ac87915d736bba3
new file mode 100644
index 0000000..ea02afa
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/17eeaca784409adbe43365c32ac87915d736bba3
@@ -0,0 +1,2 @@
+xxyyz%øyzŠ[zxy'z
+
diff --git a/test/core/support/percent_decode_corpus/2040c1ff65f52a7ae668c2c8f324de5dacc9d695 b/test/core/support/percent_decode_corpus/2040c1ff65f52a7ae668c2c8f324de5dacc9d695
new file mode 100644
index 0000000..9e9b466
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/2040c1ff65f52a7ae668c2c8f324de5dacc9d695
@@ -0,0 +1 @@
+xx;x_%;:Ê%C)x_%C88c8E'yzŠ8
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/26b0d1da23027ae54db96e125e4a9e98842d77fb b/test/core/support/percent_decode_corpus/26b0d1da23027ae54db96e125e4a9e98842d77fb
new file mode 100644
index 0000000..88c739e
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/26b0d1da23027ae54db96e125e4a9e98842d77fb
@@ -0,0 +1 @@
+))'x;x_%C88xy(Pyz)
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/2a089c0db45acdb4c6ed8e7ff81ca7235792c0b9 b/test/core/support/percent_decode_corpus/2a089c0db45acdb4c6ed8e7ff81ca7235792c0b9
new file mode 100644
index 0000000..5e6f546
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/2a089c0db45acdb4c6ed8e7ff81ca7235792c0b9
@@ -0,0 +1 @@
+_x;x)x;x_x;x_%88%8888:
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/35b7b3bc3a740d5c3abca0d75b53f0e1e1ee998a b/test/core/support/percent_decode_corpus/35b7b3bc3a740d5c3abca0d75b53f0e1e1ee998a
new file mode 100644
index 0000000..71d688b
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/35b7b3bc3a740d5c3abca0d75b53f0e1e1ee998a
@@ -0,0 +1 @@
+x8
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/36367ba1adba47a1cbc3a88707fde8cc7abdc248 b/test/core/support/percent_decode_corpus/36367ba1adba47a1cbc3a88707fde8cc7abdc248
new file mode 100644
index 0000000..5a89a07
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/36367ba1adba47a1cbc3a88707fde8cc7abdc248
@@ -0,0 +1 @@
+x);x(_%88x;x_%88
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/39c2ba51548a0beaf0d6d1164531f1447dc311b5 b/test/core/support/percent_decode_corpus/39c2ba51548a0beaf0d6d1164531f1447dc311b5
new file mode 100644
index 0000000..cfa2be9
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/39c2ba51548a0beaf0d6d1164531f1447dc311b5
@@ -0,0 +1 @@
+)x;x_x;x_%88%88:
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/56d08fea787c041395c6697ce26cfbc0decbe688 b/test/core/support/percent_decode_corpus/56d08fea787c041395c6697ce26cfbc0decbe688
new file mode 100644
index 0000000..c1ddf65
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/56d08fea787c041395c6697ce26cfbc0decbe688
@@ -0,0 +1 @@
+%cyzŠ
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/678d981fdabb9f0d6640235cf1719dd1e1e66ae9 b/test/core/support/percent_decode_corpus/678d981fdabb9f0d6640235cf1719dd1e1e66ae9
new file mode 100644
index 0000000..dc427d1
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/678d981fdabb9f0d6640235cf1719dd1e1e66ae9
@@ -0,0 +1 @@
+%øyzŠ
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/68751961609ec010565de0aa87521dcbf0722c5d b/test/core/support/percent_decode_corpus/68751961609ec010565de0aa87521dcbf0722c5d
new file mode 100644
index 0000000..154449d
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/68751961609ec010565de0aa87521dcbf0722c5d
@@ -0,0 +1 @@
+Ê:%Ec
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/7875c06c6f03c9aa2f8e9c59f8d8957c8a32e759 b/test/core/support/percent_decode_corpus/7875c06c6f03c9aa2f8e9c59f8d8957c8a32e759
new file mode 100644
index 0000000..841ced8
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/7875c06c6f03c9aa2f8e9c59f8d8957c8a32e759
@@ -0,0 +1,2 @@
+xxyyz!úyzŠ[zxy'zyz
+Š
diff --git a/test/core/support/percent_decode_corpus/7b302090e090a5829b6d1dd7be30bd4e36a7e60f b/test/core/support/percent_decode_corpus/7b302090e090a5829b6d1dd7be30bd4e36a7e60f
new file mode 100644
index 0000000..6790bc2
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/7b302090e090a5829b6d1dd7be30bd4e36a7e60f
@@ -0,0 +1 @@
+x;:Ê%)x_%C8cE'yzŠ8
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/875e1022169c9e4c541a9ad894e69e989df22ba1 b/test/core/support/percent_decode_corpus/875e1022169c9e4c541a9ad894e69e989df22ba1
new file mode 100644
index 0000000..1625d0a
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/875e1022169c9e4c541a9ad894e69e989df22ba1
@@ -0,0 +1 @@
+x;x_%88
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/8c1051ce066f5a26de9a9d133180621d0da957b4 b/test/core/support/percent_decode_corpus/8c1051ce066f5a26de9a9d133180621d0da957b4
new file mode 100644
index 0000000..125c330
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/8c1051ce066f5a26de9a9d133180621d0da957b4
@@ -0,0 +1 @@
+)))'x;x_%C88)'x;x_%C89xyyzxyyz)
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/8e084e628ab83a18ac7ca7cb3506525263655c63 b/test/core/support/percent_decode_corpus/8e084e628ab83a18ac7ca7cb3506525263655c63
new file mode 100644
index 0000000..6e6f08c
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/8e084e628ab83a18ac7ca7cb3506525263655c63
@@ -0,0 +1 @@
+))'x;x_%C88xyyz)
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/9d316c4675f40ddccaf8f1cc7aea94170b1e4223 b/test/core/support/percent_decode_corpus/9d316c4675f40ddccaf8f1cc7aea94170b1e4223
new file mode 100644
index 0000000..ab4a1c7
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/9d316c4675f40ddccaf8f1cc7aea94170b1e4223
@@ -0,0 +1 @@
+x%8
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/ad1c7c11d18a7d116e2c2ef4d4c5afb1270836ae b/test/core/support/percent_decode_corpus/ad1c7c11d18a7d116e2c2ef4d4c5afb1270836ae
new file mode 100644
index 0000000..4ac1945
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/ad1c7c11d18a7d116e2c2ef4d4c5afb1270836ae
@@ -0,0 +1 @@
+x);x(_%88x;x_xxyyz
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/b471f94aa4facf502e622e4a248f1ba4063ae681 b/test/core/support/percent_decode_corpus/b471f94aa4facf502e622e4a248f1ba4063ae681
new file mode 100644
index 0000000..5c673ae
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/b471f94aa4facf502e622e4a248f1ba4063ae681
@@ -0,0 +1 @@
+Ê%ccyzyzŠ
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/bf52ece030f16136d46e0dc97f58d60a0d8a1f0b b/test/core/support/percent_decode_corpus/bf52ece030f16136d46e0dc97f58d60a0d8a1f0b
new file mode 100644
index 0000000..e478275
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/bf52ece030f16136d46e0dc97f58d60a0d8a1f0b
@@ -0,0 +1,2 @@
+)'xyyz!úyzŠ[zxÊ%ccyzyzy'z*zŠ
+Š
diff --git a/test/core/support/percent_decode_corpus/d5b2a7177339ba2b7ce2f60e5f4459bef1e72758 b/test/core/support/percent_decode_corpus/d5b2a7177339ba2b7ce2f60e5f4459bef1e72758
new file mode 100644
index 0000000..c73cbfe
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/d5b2a7177339ba2b7ce2f60e5f4459bef1e72758
@@ -0,0 +1,2 @@
+)'xyyz)úyzŠ[zxÊ%cCyzyzy'z*zŠ
+Š
diff --git a/test/core/support/percent_decode_corpus/de867b64c54a7ed773dc611fc5cd2f17c5433113 b/test/core/support/percent_decode_corpus/de867b64c54a7ed773dc611fc5cd2f17c5433113
new file mode 100644
index 0000000..f9f7246
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/de867b64c54a7ed773dc611fc5cd2f17c5433113
@@ -0,0 +1,2 @@
+xxyyz%%øyzŠ[zxy'zyz
+Š
diff --git a/test/core/support/percent_decode_corpus/e3948dbe004950591630dd5c52f4e0fcbd5e388a b/test/core/support/percent_decode_corpus/e3948dbe004950591630dd5c52f4e0fcbd5e388a
new file mode 100644
index 0000000..83ac46d
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/e3948dbe004950591630dd5c52f4e0fcbd5e388a
@@ -0,0 +1 @@
+Ê:%Dx;:Ê%)x_%C8cc
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/e7064f0b80f61dbc65915311032d27baa569ae2a b/test/core/support/percent_decode_corpus/e7064f0b80f61dbc65915311032d27baa569ae2a
new file mode 100644
index 0000000..e8a0f87
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/e7064f0b80f61dbc65915311032d27baa569ae2a
@@ -0,0 +1 @@
+)
\ No newline at end of file
diff --git a/test/core/support/percent_decode_corpus/xyz b/test/core/support/percent_decode_corpus/xyz
new file mode 100644
index 0000000..cd470e6
--- /dev/null
+++ b/test/core/support/percent_decode_corpus/xyz
@@ -0,0 +1 @@
+xyz
diff --git a/test/core/support/percent_decode_fuzzer.c b/test/core/support/percent_decode_fuzzer.c
new file mode 100644
index 0000000..3e02980
--- /dev/null
+++ b/test/core/support/percent_decode_fuzzer.c
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright 2016, 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 <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/support/percent_encoding.h"
+#include "test/core/util/memory_counters.h"
+
+bool squelch = true;
+bool leak_check = true;
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  struct grpc_memory_counters counters;
+  grpc_memory_counters_init();
+  gpr_slice input = gpr_slice_from_copied_buffer((const char *)data, size);
+  gpr_slice output;
+  if (gpr_strict_percent_decode_slice(
+          input, gpr_url_percent_encoding_unreserved_bytes, &output)) {
+    gpr_slice_unref(output);
+  }
+  if (gpr_strict_percent_decode_slice(
+          input, gpr_compatible_percent_encoding_unreserved_bytes, &output)) {
+    gpr_slice_unref(output);
+  }
+  gpr_slice_unref(gpr_permissive_percent_decode_slice(input));
+  gpr_slice_unref(input);
+  counters = grpc_memory_counters_snapshot();
+  grpc_memory_counters_destroy();
+  GPR_ASSERT(counters.total_size_relative == 0);
+  return 0;
+}
diff --git a/test/core/support/percent_encode_corpus/0d3ee7fa54e6c66103965fd4409b044ba7db6c3f b/test/core/support/percent_encode_corpus/0d3ee7fa54e6c66103965fd4409b044ba7db6c3f
new file mode 100644
index 0000000..d09c4a0
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/0d3ee7fa54e6c66103965fd4409b044ba7db6c3f
@@ -0,0 +1,3 @@
+_x;7y
+xyz')S)xy-zý
+Æ*
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/2e7ccf75e27b9501e3b28cf1c50ed0c45ab7c226 b/test/core/support/percent_encode_corpus/2e7ccf75e27b9501e3b28cf1c50ed0c45ab7c226
new file mode 100644
index 0000000..4d0c38d
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/2e7ccf75e27b9501e3b28cf1c50ed0c45ab7c226
@@ -0,0 +1 @@
+xyx
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/55bb859f3942c462b03b7cbcf22ab4a0ac9705cf b/test/core/support/percent_encode_corpus/55bb859f3942c462b03b7cbcf22ab4a0ac9705cf
new file mode 100644
index 0000000..fc6e933
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/55bb859f3942c462b03b7cbcf22ab4a0ac9705cf
@@ -0,0 +1 @@
+.yx.yxxxyzxyyzxy
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/56070cecd54c845b6d4334953b17b712eb000d93 b/test/core/support/percent_encode_corpus/56070cecd54c845b6d4334953b17b712eb000d93
new file mode 100644
index 0000000..6823c73
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/56070cecd54c845b6d4334953b17b712eb000d93
@@ -0,0 +1 @@
+xyrxyxyzxxyzxyzxyxyy
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/61f50e891bf7ff5eb7a7af206f1e25d77f8756e7 b/test/core/support/percent_encode_corpus/61f50e891bf7ff5eb7a7af206f1e25d77f8756e7
new file mode 100644
index 0000000..a65cbb4
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/61f50e891bf7ff5eb7a7af206f1e25d77f8756e7
@@ -0,0 +1,3 @@
+xy
+xyz
+)S-Æþ
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/6e0c60cefc704c7940e475a87dd9ae423061cb5a b/test/core/support/percent_encode_corpus/6e0c60cefc704c7940e475a87dd9ae423061cb5a
new file mode 100644
index 0000000..8d031d7
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/6e0c60cefc704c7940e475a87dd9ae423061cb5a
@@ -0,0 +1,3 @@
+xy
+xyz
+)S)Æ*
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/7271ebcc6d22a0f186f7bc3c1973a7ed1bec8d8e b/test/core/support/percent_encode_corpus/7271ebcc6d22a0f186f7bc3c1973a7ed1bec8d8e
new file mode 100644
index 0000000..4d82ca3
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/7271ebcc6d22a0f186f7bc3c1973a7ed1bec8d8e
@@ -0,0 +1,4 @@
+x;7y
+xyz
+)S)xyz
+Æ*
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/74c83ece3e2920a67593a9be9c82468f16cbb969 b/test/core/support/percent_encode_corpus/74c83ece3e2920a67593a9be9c82468f16cbb969
new file mode 100644
index 0000000..bb7f4ae
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/74c83ece3e2920a67593a9be9c82468f16cbb969
@@ -0,0 +1 @@
+xyzxy
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/98e004fd2a9f141a7a019720820080e12d637c06 b/test/core/support/percent_encode_corpus/98e004fd2a9f141a7a019720820080e12d637c06
new file mode 100644
index 0000000..50879d0
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/98e004fd2a9f141a7a019720820080e12d637c06
@@ -0,0 +1,3 @@
+xy
+xz
+)Sxy-Æzx_yþ
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/ba2c1e98227aa21ea3bb2ca4d0e504119717da8b b/test/core/support/percent_encode_corpus/ba2c1e98227aa21ea3bb2ca4d0e504119717da8b
new file mode 100644
index 0000000..dc1ab9b
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/ba2c1e98227aa21ea3bb2ca4d0e504119717da8b
@@ -0,0 +1,3 @@
+_x;7y
+xyz')S)xyz
+Æ*
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/c16b9fd45370d4afb5d3ebd307a6e263c25ffd45 b/test/core/support/percent_encode_corpus/c16b9fd45370d4afb5d3ebd307a6e263c25ffd45
new file mode 100644
index 0000000..3476e0b
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/c16b9fd45370d4afb5d3ebd307a6e263c25ffd45
@@ -0,0 +1,2 @@
+xyz
+)S
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/d58c3cd4eab9b6d2343abfa1c25c90a383fe0ec3 b/test/core/support/percent_encode_corpus/d58c3cd4eab9b6d2343abfa1c25c90a383fe0ec3
new file mode 100644
index 0000000..822d50a
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/d58c3cd4eab9b6d2343abfa1c25c90a383fe0ec3
@@ -0,0 +1 @@
+.yx
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/e2619218ede30d2b7b8ecd601a9f0ae754b728b4 b/test/core/support/percent_encode_corpus/e2619218ede30d2b7b8ecd601a9f0ae754b728b4
new file mode 100644
index 0000000..101639c
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/e2619218ede30d2b7b8ecd601a9f0ae754b728b4
@@ -0,0 +1,4 @@
+x;y
+xyz
+)S)xyz
+Æ*
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/f93b3653e453f0e3eea3198001be6ce46e64bd21 b/test/core/support/percent_encode_corpus/f93b3653e453f0e3eea3198001be6ce46e64bd21
new file mode 100644
index 0000000..6e07ab3
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/f93b3653e453f0e3eea3198001be6ce46e64bd21
@@ -0,0 +1,5 @@
+x;y
+xøyz
+)S)xyz
+Æ.y~
+)S
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/fd41d029c7682ad3d1c40a9fd017a4c85b673a54 b/test/core/support/percent_encode_corpus/fd41d029c7682ad3d1c40a9fd017a4c85b673a54
new file mode 100644
index 0000000..13d7fab
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/fd41d029c7682ad3d1c40a9fd017a4c85b673a54
@@ -0,0 +1,3 @@
+xy
+xyz
+)S)S
\ No newline at end of file
diff --git a/test/core/support/percent_encode_corpus/xyz b/test/core/support/percent_encode_corpus/xyz
new file mode 100644
index 0000000..cd470e6
--- /dev/null
+++ b/test/core/support/percent_encode_corpus/xyz
@@ -0,0 +1 @@
+xyz
diff --git a/test/core/support/percent_encode_fuzzer.c b/test/core/support/percent_encode_fuzzer.c
new file mode 100644
index 0000000..c954823
--- /dev/null
+++ b/test/core/support/percent_encode_fuzzer.c
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2016, 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 <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/support/percent_encoding.h"
+#include "test/core/util/memory_counters.h"
+
+bool squelch = true;
+bool leak_check = true;
+
+static void test(const uint8_t *data, size_t size, const uint8_t *dict) {
+  struct grpc_memory_counters counters;
+  grpc_memory_counters_init();
+  gpr_slice input = gpr_slice_from_copied_buffer((const char *)data, size);
+  gpr_slice output = gpr_percent_encode_slice(input, dict);
+  gpr_slice decoded_output;
+  // encoder must always produce decodable output
+  GPR_ASSERT(gpr_strict_percent_decode_slice(output, dict, &decoded_output));
+  gpr_slice permissive_decoded_output =
+      gpr_permissive_percent_decode_slice(output);
+  // and decoded output must always match the input
+  GPR_ASSERT(gpr_slice_cmp(input, decoded_output) == 0);
+  GPR_ASSERT(gpr_slice_cmp(input, permissive_decoded_output) == 0);
+  gpr_slice_unref(input);
+  gpr_slice_unref(output);
+  gpr_slice_unref(decoded_output);
+  gpr_slice_unref(permissive_decoded_output);
+  counters = grpc_memory_counters_snapshot();
+  grpc_memory_counters_destroy();
+  GPR_ASSERT(counters.total_size_relative == 0);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  test(data, size, gpr_url_percent_encoding_unreserved_bytes);
+  test(data, size, gpr_compatible_percent_encoding_unreserved_bytes);
+  return 0;
+}
diff --git a/test/core/support/percent_encoding_test.c b/test/core/support/percent_encoding_test.c
new file mode 100644
index 0000000..ab5f3f2
--- /dev/null
+++ b/test/core/support/percent_encoding_test.c
@@ -0,0 +1,157 @@
+/*
+ *
+ * Copyright 2016, 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/lib/support/percent_encoding.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/support/string.h"
+#include "test/core/util/test_config.h"
+
+#define TEST_VECTOR(raw, encoded, dict) \
+  test_vector(raw, sizeof(raw) - 1, encoded, sizeof(encoded) - 1, dict)
+
+#define TEST_NONCONFORMANT_VECTOR(encoded, permissive_unencoded, dict) \
+  test_nonconformant_vector(encoded, sizeof(encoded) - 1,              \
+                            permissive_unencoded,                      \
+                            sizeof(permissive_unencoded) - 1, dict)
+
+static void test_vector(const char *raw, size_t raw_length, const char *encoded,
+                        size_t encoded_length, const uint8_t *dict) {
+  char *raw_msg = gpr_dump(raw, raw_length, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+  char *encoded_msg =
+      gpr_dump(encoded, encoded_length, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+  gpr_log(GPR_DEBUG, "Trial:\nraw = %s\nencoded = %s", raw_msg, encoded_msg);
+  gpr_free(raw_msg);
+  gpr_free(encoded_msg);
+
+  gpr_slice raw_slice = gpr_slice_from_copied_buffer(raw, raw_length);
+  gpr_slice encoded_slice =
+      gpr_slice_from_copied_buffer(encoded, encoded_length);
+  gpr_slice raw2encoded_slice = gpr_percent_encode_slice(raw_slice, dict);
+  gpr_slice encoded2raw_slice;
+  GPR_ASSERT(
+      gpr_strict_percent_decode_slice(encoded_slice, dict, &encoded2raw_slice));
+  gpr_slice encoded2raw_permissive_slice =
+      gpr_permissive_percent_decode_slice(encoded_slice);
+
+  char *raw2encoded_msg =
+      gpr_dump_slice(raw2encoded_slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+  char *encoded2raw_msg =
+      gpr_dump_slice(encoded2raw_slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+  char *encoded2raw_permissive_msg = gpr_dump_slice(
+      encoded2raw_permissive_slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+  gpr_log(GPR_DEBUG,
+          "Result:\nraw2encoded = %s\nencoded2raw = %s\nencoded2raw_permissive "
+          "= %s",
+          raw2encoded_msg, encoded2raw_msg, encoded2raw_permissive_msg);
+  gpr_free(raw2encoded_msg);
+  gpr_free(encoded2raw_msg);
+  gpr_free(encoded2raw_permissive_msg);
+
+  GPR_ASSERT(0 == gpr_slice_cmp(raw_slice, encoded2raw_slice));
+  GPR_ASSERT(0 == gpr_slice_cmp(raw_slice, encoded2raw_permissive_slice));
+  GPR_ASSERT(0 == gpr_slice_cmp(encoded_slice, raw2encoded_slice));
+
+  gpr_slice_unref(encoded2raw_slice);
+  gpr_slice_unref(encoded2raw_permissive_slice);
+  gpr_slice_unref(raw2encoded_slice);
+  gpr_slice_unref(raw_slice);
+  gpr_slice_unref(encoded_slice);
+}
+
+static void test_nonconformant_vector(const char *encoded,
+                                      size_t encoded_length,
+                                      const char *permissive_unencoded,
+                                      size_t permissive_unencoded_length,
+                                      const uint8_t *dict) {
+  char *permissive_unencoded_msg =
+      gpr_dump(permissive_unencoded, permissive_unencoded_length,
+               GPR_DUMP_HEX | GPR_DUMP_ASCII);
+  char *encoded_msg =
+      gpr_dump(encoded, encoded_length, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+  gpr_log(GPR_DEBUG, "Trial:\nraw = %s\nencoded = %s", permissive_unencoded_msg,
+          encoded_msg);
+  gpr_free(permissive_unencoded_msg);
+  gpr_free(encoded_msg);
+
+  gpr_slice permissive_unencoded_slice = gpr_slice_from_copied_buffer(
+      permissive_unencoded, permissive_unencoded_length);
+  gpr_slice encoded_slice =
+      gpr_slice_from_copied_buffer(encoded, encoded_length);
+  gpr_slice encoded2raw_slice;
+  GPR_ASSERT(!gpr_strict_percent_decode_slice(encoded_slice, dict,
+                                              &encoded2raw_slice));
+  gpr_slice encoded2raw_permissive_slice =
+      gpr_permissive_percent_decode_slice(encoded_slice);
+
+  char *encoded2raw_permissive_msg = gpr_dump_slice(
+      encoded2raw_permissive_slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+  gpr_log(GPR_DEBUG, "Result:\nencoded2raw_permissive = %s",
+          encoded2raw_permissive_msg);
+  gpr_free(encoded2raw_permissive_msg);
+
+  GPR_ASSERT(0 == gpr_slice_cmp(permissive_unencoded_slice,
+                                encoded2raw_permissive_slice));
+
+  gpr_slice_unref(permissive_unencoded_slice);
+  gpr_slice_unref(encoded2raw_permissive_slice);
+  gpr_slice_unref(encoded_slice);
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  TEST_VECTOR(
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~",
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~",
+      gpr_url_percent_encoding_unreserved_bytes);
+  TEST_VECTOR("\x00", "%00", gpr_url_percent_encoding_unreserved_bytes);
+  TEST_VECTOR("\x01", "%01", gpr_url_percent_encoding_unreserved_bytes);
+  TEST_VECTOR("a b", "a%20b", gpr_url_percent_encoding_unreserved_bytes);
+  TEST_VECTOR(" b", "%20b", gpr_url_percent_encoding_unreserved_bytes);
+  TEST_VECTOR("a b", "a b", gpr_compatible_percent_encoding_unreserved_bytes);
+  TEST_VECTOR(" b", " b", gpr_compatible_percent_encoding_unreserved_bytes);
+  TEST_VECTOR("\x0f", "%0F", gpr_url_percent_encoding_unreserved_bytes);
+  TEST_VECTOR("\xff", "%FF", gpr_url_percent_encoding_unreserved_bytes);
+  TEST_VECTOR("\xee", "%EE", gpr_url_percent_encoding_unreserved_bytes);
+  TEST_NONCONFORMANT_VECTOR("%", "%",
+                            gpr_url_percent_encoding_unreserved_bytes);
+  TEST_NONCONFORMANT_VECTOR("%A", "%A",
+                            gpr_url_percent_encoding_unreserved_bytes);
+  TEST_NONCONFORMANT_VECTOR("%AG", "%AG",
+                            gpr_url_percent_encoding_unreserved_bytes);
+  TEST_NONCONFORMANT_VECTOR("\0", "\0",
+                            gpr_url_percent_encoding_unreserved_bytes);
+  return 0;
+}
diff --git a/test/core/surface/channel_create_test.c b/test/core/surface/channel_create_test.c
index 450cc37..580eb30 100644
--- a/test/core/surface/channel_create_test.c
+++ b/test/core/surface/channel_create_test.c
@@ -40,7 +40,7 @@
   grpc_channel *chan;
   /* avoid default prefix */
   grpc_resolver_registry_shutdown();
-  grpc_resolver_registry_init("");
+  grpc_resolver_registry_init();
 
   chan = grpc_insecure_channel_create("blah://blah", NULL, NULL);
   GPR_ASSERT(chan != NULL);
diff --git a/test/core/surface/invalid_channel_args_test.c b/test/core/surface/invalid_channel_args_test.c
index 1b1b8b8..0640879 100644
--- a/test/core/surface/invalid_channel_args_test.c
+++ b/test/core/surface/invalid_channel_args_test.c
@@ -84,38 +84,6 @@
 
 static void test_no_error_message(void) { one_test(NULL, NULL); }
 
-static void test_max_message_length_type(void) {
-  grpc_arg client_arg;
-  grpc_channel_args client_args;
-  char *expected_error_message;
-
-  client_arg.type = GRPC_ARG_STRING;
-  client_arg.key = GRPC_ARG_MAX_MESSAGE_LENGTH;
-  client_arg.value.string = NULL;
-
-  client_args.num_args = 1;
-  client_args.args = &client_arg;
-  expected_error_message = compose_error_string(
-      GRPC_ARG_MAX_MESSAGE_LENGTH, " ignored: it must be an integer");
-  one_test(&client_args, expected_error_message);
-}
-
-static void test_max_message_length_negative(void) {
-  grpc_arg client_arg;
-  grpc_channel_args client_args;
-  char *expected_error_message;
-
-  client_arg.type = GRPC_ARG_INTEGER;
-  client_arg.key = GRPC_ARG_MAX_MESSAGE_LENGTH;
-  client_arg.value.integer = -1;
-
-  client_args.num_args = 1;
-  client_args.args = &client_arg;
-  expected_error_message = compose_error_string(GRPC_ARG_MAX_MESSAGE_LENGTH,
-                                                " ignored: it must be >= 0");
-  one_test(&client_args, expected_error_message);
-}
-
 static void test_default_authority_type(void) {
   grpc_arg client_arg;
   grpc_channel_args client_args;
@@ -174,8 +142,6 @@
   gpr_set_log_function(log_error_sink);
 
   test_no_error_message();
-  test_max_message_length_type();
-  test_max_message_length_negative();
   test_default_authority_type();
   test_ssl_name_override_type();
   test_ssl_name_override_failed();
diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c
index f36f980..6afcefc 100644
--- a/test/core/surface/lame_client_test.c
+++ b/test/core/surface/lame_client_test.c
@@ -49,32 +49,31 @@
 
 void verify_connectivity(grpc_exec_ctx *exec_ctx, void *arg,
                          grpc_error *error) {
-  grpc_transport_op *op = arg;
-  GPR_ASSERT(GRPC_CHANNEL_SHUTDOWN == *op->connectivity_state);
+  grpc_connectivity_state *state = arg;
+  GPR_ASSERT(GRPC_CHANNEL_SHUTDOWN == *state);
   GPR_ASSERT(error == GRPC_ERROR_NONE);
 }
 
 void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {}
 
 void test_transport_op(grpc_channel *channel) {
-  grpc_transport_op op;
+  grpc_transport_op *op;
   grpc_channel_element *elem;
   grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
-  memset(&op, 0, sizeof(op));
-  grpc_closure_init(&transport_op_cb, verify_connectivity, &op);
+  grpc_closure_init(&transport_op_cb, verify_connectivity, &state);
 
-  op.on_connectivity_state_change = &transport_op_cb;
-  op.connectivity_state = &state;
+  op = grpc_make_transport_op(NULL);
+  op->on_connectivity_state_change = &transport_op_cb;
+  op->connectivity_state = &state;
   elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
-  elem->filter->start_transport_op(&exec_ctx, elem, &op);
+  elem->filter->start_transport_op(&exec_ctx, elem, op);
   grpc_exec_ctx_finish(&exec_ctx);
 
-  memset(&op, 0, sizeof(op));
   grpc_closure_init(&transport_op_cb, do_nothing, NULL);
-  op.on_consumed = &transport_op_cb;
-  elem->filter->start_transport_op(&exec_ctx, elem, &op);
+  op = grpc_make_transport_op(&transport_op_cb);
+  elem->filter->start_transport_op(&exec_ctx, elem, op);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
@@ -132,7 +131,7 @@
   GPR_ASSERT(GRPC_CALL_OK == error);
 
   /* the call should immediately fail */
-  cq_expect_completion(cqv, tag(1), 0);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 0);
   cq_verify(cqv);
 
   memset(ops, 0, sizeof(ops));
@@ -149,7 +148,7 @@
   GPR_ASSERT(GRPC_CALL_OK == error);
 
   /* the call should immediately fail */
-  cq_expect_completion(cqv, tag(2), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(2), 1);
   cq_verify(cqv);
 
   peer = grpc_call_get_peer(call);
diff --git a/test/core/surface/public_headers_must_be_c89.c b/test/core/surface/public_headers_must_be_c89.c
index 3eeb55d..53bdf61 100644
--- a/test/core/surface/public_headers_must_be_c89.c
+++ b/test/core/surface/public_headers_must_be_c89.c
@@ -38,22 +38,18 @@
 #include <grpc/grpc.h>
 #include <grpc/grpc_security.h>
 #include <grpc/grpc_security_constants.h>
-#include <grpc/impl/codegen/alloc.h>
 #include <grpc/impl/codegen/atm.h>
-#include <grpc/impl/codegen/byte_buffer.h>
 #include <grpc/impl/codegen/byte_buffer_reader.h>
 #include <grpc/impl/codegen/compression_types.h>
 #include <grpc/impl/codegen/connectivity_state.h>
+#include <grpc/impl/codegen/gpr_types.h>
 #include <grpc/impl/codegen/grpc_types.h>
-#include <grpc/impl/codegen/log.h>
 #include <grpc/impl/codegen/port_platform.h>
 #include <grpc/impl/codegen/propagation_bits.h>
 #include <grpc/impl/codegen/slice.h>
-#include <grpc/impl/codegen/slice_buffer.h>
 #include <grpc/impl/codegen/status.h>
 #include <grpc/impl/codegen/sync.h>
 #include <grpc/impl/codegen/sync_generic.h>
-#include <grpc/impl/codegen/time.h>
 #include <grpc/status.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/atm.h>
diff --git a/test/core/surface/secure_channel_create_test.c b/test/core/surface/secure_channel_create_test.c
index b952503..f8a9a64 100644
--- a/test/core/surface/secure_channel_create_test.c
+++ b/test/core/surface/secure_channel_create_test.c
@@ -46,7 +46,7 @@
   grpc_channel *chan;
   grpc_channel_credentials *creds;
   grpc_resolver_registry_shutdown();
-  grpc_resolver_registry_init("");
+  grpc_resolver_registry_init();
 
   creds = grpc_fake_transport_security_credentials_create();
   chan = grpc_secure_channel_create(creds, "blah://blah", NULL, NULL);
diff --git a/test/core/transport/chttp2/hpack_encoder_test.c b/test/core/transport/chttp2/hpack_encoder_test.c
index 186bb64..1c1c748 100644
--- a/test/core/transport/chttp2/hpack_encoder_test.c
+++ b/test/core/transport/chttp2/hpack_encoder_test.c
@@ -97,7 +97,7 @@
 
   grpc_transport_one_way_stats stats;
   memset(&stats, 0, sizeof(stats));
-  grpc_chttp2_encode_header(&g_compressor, 0xdeadbeef, &b, eof, &stats,
+  grpc_chttp2_encode_header(&g_compressor, 0xdeadbeef, &b, eof, 16384, &stats,
                             &output);
   merged = grpc_slice_merge(output.slices, output.count);
   gpr_slice_buffer_destroy(&output);
@@ -202,7 +202,8 @@
 
   grpc_transport_one_way_stats stats;
   memset(&stats, 0, sizeof(stats));
-  grpc_chttp2_encode_header(&g_compressor, 0xdeadbeef, &b, 0, &stats, &output);
+  grpc_chttp2_encode_header(&g_compressor, 0xdeadbeef, &b, 0, 16384, &stats,
+                            &output);
   gpr_slice_buffer_destroy(&output);
   grpc_metadata_batch_destroy(&b);
 
diff --git a/test/core/transport/chttp2/timeout_encoding_test.c b/test/core/transport/chttp2/timeout_encoding_test.c
deleted file mode 100644
index 6763993..0000000
--- a/test/core/transport/chttp2/timeout_encoding_test.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include "src/core/ext/transport/chttp2/transport/timeout_encoding.h"
-
-#include <stdio.h>
-#include <string.h>
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-#include <grpc/support/string_util.h>
-#include <grpc/support/useful.h>
-#include "src/core/lib/support/string.h"
-#include "test/core/util/test_config.h"
-
-#define LOG_TEST(x) gpr_log(GPR_INFO, "%s", x)
-
-static void assert_encodes_as(gpr_timespec ts, const char *s) {
-  char buffer[GRPC_CHTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
-  grpc_chttp2_encode_timeout(ts, buffer);
-  gpr_log(GPR_INFO, "check '%s' == '%s'", buffer, s);
-  GPR_ASSERT(0 == strcmp(buffer, s));
-}
-
-void test_encoding(void) {
-  LOG_TEST("test_encoding");
-  assert_encodes_as(gpr_time_from_micros(-1, GPR_TIMESPAN), "1n");
-  assert_encodes_as(gpr_time_from_seconds(-10, GPR_TIMESPAN), "1n");
-  assert_encodes_as(gpr_time_from_nanos(10, GPR_TIMESPAN), "10n");
-  assert_encodes_as(gpr_time_from_nanos(999999999, GPR_TIMESPAN), "1S");
-  assert_encodes_as(gpr_time_from_micros(1, GPR_TIMESPAN), "1u");
-  assert_encodes_as(gpr_time_from_micros(10, GPR_TIMESPAN), "10u");
-  assert_encodes_as(gpr_time_from_micros(100, GPR_TIMESPAN), "100u");
-  assert_encodes_as(gpr_time_from_micros(890, GPR_TIMESPAN), "890u");
-  assert_encodes_as(gpr_time_from_micros(900, GPR_TIMESPAN), "900u");
-  assert_encodes_as(gpr_time_from_micros(901, GPR_TIMESPAN), "901u");
-  assert_encodes_as(gpr_time_from_millis(1, GPR_TIMESPAN), "1m");
-  assert_encodes_as(gpr_time_from_millis(2, GPR_TIMESPAN), "2m");
-  assert_encodes_as(gpr_time_from_micros(10001, GPR_TIMESPAN), "10100u");
-  assert_encodes_as(gpr_time_from_micros(999999, GPR_TIMESPAN), "1S");
-  assert_encodes_as(gpr_time_from_millis(1000, GPR_TIMESPAN), "1S");
-  assert_encodes_as(gpr_time_from_millis(2000, GPR_TIMESPAN), "2S");
-  assert_encodes_as(gpr_time_from_millis(2500, GPR_TIMESPAN), "2500m");
-  assert_encodes_as(gpr_time_from_millis(59900, GPR_TIMESPAN), "59900m");
-  assert_encodes_as(gpr_time_from_seconds(50, GPR_TIMESPAN), "50S");
-  assert_encodes_as(gpr_time_from_seconds(59, GPR_TIMESPAN), "59S");
-  assert_encodes_as(gpr_time_from_seconds(60, GPR_TIMESPAN), "1M");
-  assert_encodes_as(gpr_time_from_seconds(80, GPR_TIMESPAN), "80S");
-  assert_encodes_as(gpr_time_from_seconds(90, GPR_TIMESPAN), "90S");
-  assert_encodes_as(gpr_time_from_minutes(2, GPR_TIMESPAN), "2M");
-  assert_encodes_as(gpr_time_from_minutes(20, GPR_TIMESPAN), "20M");
-  assert_encodes_as(gpr_time_from_hours(1, GPR_TIMESPAN), "1H");
-  assert_encodes_as(gpr_time_from_hours(10, GPR_TIMESPAN), "10H");
-  assert_encodes_as(gpr_time_from_seconds(1000000000, GPR_TIMESPAN),
-                    "1000000000S");
-}
-
-static void assert_decodes_as(const char *buffer, gpr_timespec expected) {
-  gpr_timespec got;
-  gpr_log(GPR_INFO, "check decoding '%s'", buffer);
-  GPR_ASSERT(1 == grpc_chttp2_decode_timeout(buffer, &got));
-  GPR_ASSERT(0 == gpr_time_cmp(got, expected));
-}
-
-void decode_suite(char ext,
-                  gpr_timespec (*answer)(int64_t x, gpr_clock_type clock)) {
-  long test_vals[] = {1,       12,       123,       1234,     12345,   123456,
-                      1234567, 12345678, 123456789, 98765432, 9876543, 987654,
-                      98765,   9876,     987,       98,       9};
-  unsigned i;
-  char *input;
-  for (i = 0; i < GPR_ARRAY_SIZE(test_vals); i++) {
-    gpr_asprintf(&input, "%ld%c", test_vals[i], ext);
-    assert_decodes_as(input, answer(test_vals[i], GPR_TIMESPAN));
-    gpr_free(input);
-
-    gpr_asprintf(&input, "   %ld%c", test_vals[i], ext);
-    assert_decodes_as(input, answer(test_vals[i], GPR_TIMESPAN));
-    gpr_free(input);
-
-    gpr_asprintf(&input, "%ld %c", test_vals[i], ext);
-    assert_decodes_as(input, answer(test_vals[i], GPR_TIMESPAN));
-    gpr_free(input);
-
-    gpr_asprintf(&input, "%ld %c  ", test_vals[i], ext);
-    assert_decodes_as(input, answer(test_vals[i], GPR_TIMESPAN));
-    gpr_free(input);
-  }
-}
-
-void test_decoding(void) {
-  LOG_TEST("test_decoding");
-  decode_suite('n', gpr_time_from_nanos);
-  decode_suite('u', gpr_time_from_micros);
-  decode_suite('m', gpr_time_from_millis);
-  decode_suite('S', gpr_time_from_seconds);
-  decode_suite('M', gpr_time_from_minutes);
-  decode_suite('H', gpr_time_from_hours);
-  assert_decodes_as("1000000000S",
-                    gpr_time_from_seconds(1000 * 1000 * 1000, GPR_TIMESPAN));
-  assert_decodes_as("1000000000000000000000u", gpr_inf_future(GPR_TIMESPAN));
-  assert_decodes_as("1000000001S", gpr_inf_future(GPR_TIMESPAN));
-  assert_decodes_as("2000000001S", gpr_inf_future(GPR_TIMESPAN));
-  assert_decodes_as("9999999999S", gpr_inf_future(GPR_TIMESPAN));
-}
-
-void test_decoding_fails(void) {
-  gpr_timespec x;
-  LOG_TEST("test_decoding_fails");
-  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("", &x));
-  GPR_ASSERT(0 == grpc_chttp2_decode_timeout(" ", &x));
-  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("x", &x));
-  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("1", &x));
-  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("1x", &x));
-  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("1ux", &x));
-  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("!", &x));
-  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("n1", &x));
-  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("-1u", &x));
-}
-
-int main(int argc, char **argv) {
-  grpc_test_init(argc, argv);
-  test_encoding();
-  test_decoding();
-  test_decoding_fails();
-  return 0;
-}
diff --git a/test/core/transport/timeout_encoding_test.c b/test/core/transport/timeout_encoding_test.c
new file mode 100644
index 0000000..b6004af
--- /dev/null
+++ b/test/core/transport/timeout_encoding_test.c
@@ -0,0 +1,157 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/transport/timeout_encoding.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/support/string.h"
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST(x) gpr_log(GPR_INFO, "%s", x)
+
+static void assert_encodes_as(gpr_timespec ts, const char *s) {
+  char buffer[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
+  grpc_http2_encode_timeout(ts, buffer);
+  gpr_log(GPR_INFO, "check '%s' == '%s'", buffer, s);
+  GPR_ASSERT(0 == strcmp(buffer, s));
+}
+
+void test_encoding(void) {
+  LOG_TEST("test_encoding");
+  assert_encodes_as(gpr_time_from_micros(-1, GPR_TIMESPAN), "1n");
+  assert_encodes_as(gpr_time_from_seconds(-10, GPR_TIMESPAN), "1n");
+  assert_encodes_as(gpr_time_from_nanos(10, GPR_TIMESPAN), "10n");
+  assert_encodes_as(gpr_time_from_nanos(999999999, GPR_TIMESPAN), "1S");
+  assert_encodes_as(gpr_time_from_micros(1, GPR_TIMESPAN), "1u");
+  assert_encodes_as(gpr_time_from_micros(10, GPR_TIMESPAN), "10u");
+  assert_encodes_as(gpr_time_from_micros(100, GPR_TIMESPAN), "100u");
+  assert_encodes_as(gpr_time_from_micros(890, GPR_TIMESPAN), "890u");
+  assert_encodes_as(gpr_time_from_micros(900, GPR_TIMESPAN), "900u");
+  assert_encodes_as(gpr_time_from_micros(901, GPR_TIMESPAN), "901u");
+  assert_encodes_as(gpr_time_from_millis(1, GPR_TIMESPAN), "1m");
+  assert_encodes_as(gpr_time_from_millis(2, GPR_TIMESPAN), "2m");
+  assert_encodes_as(gpr_time_from_micros(10001, GPR_TIMESPAN), "10100u");
+  assert_encodes_as(gpr_time_from_micros(999999, GPR_TIMESPAN), "1S");
+  assert_encodes_as(gpr_time_from_millis(1000, GPR_TIMESPAN), "1S");
+  assert_encodes_as(gpr_time_from_millis(2000, GPR_TIMESPAN), "2S");
+  assert_encodes_as(gpr_time_from_millis(2500, GPR_TIMESPAN), "2500m");
+  assert_encodes_as(gpr_time_from_millis(59900, GPR_TIMESPAN), "59900m");
+  assert_encodes_as(gpr_time_from_seconds(50, GPR_TIMESPAN), "50S");
+  assert_encodes_as(gpr_time_from_seconds(59, GPR_TIMESPAN), "59S");
+  assert_encodes_as(gpr_time_from_seconds(60, GPR_TIMESPAN), "1M");
+  assert_encodes_as(gpr_time_from_seconds(80, GPR_TIMESPAN), "80S");
+  assert_encodes_as(gpr_time_from_seconds(90, GPR_TIMESPAN), "90S");
+  assert_encodes_as(gpr_time_from_minutes(2, GPR_TIMESPAN), "2M");
+  assert_encodes_as(gpr_time_from_minutes(20, GPR_TIMESPAN), "20M");
+  assert_encodes_as(gpr_time_from_hours(1, GPR_TIMESPAN), "1H");
+  assert_encodes_as(gpr_time_from_hours(10, GPR_TIMESPAN), "10H");
+  assert_encodes_as(gpr_time_from_seconds(1000000000, GPR_TIMESPAN),
+                    "1000000000S");
+}
+
+static void assert_decodes_as(const char *buffer, gpr_timespec expected) {
+  gpr_timespec got;
+  gpr_log(GPR_INFO, "check decoding '%s'", buffer);
+  GPR_ASSERT(1 == grpc_http2_decode_timeout(buffer, &got));
+  GPR_ASSERT(0 == gpr_time_cmp(got, expected));
+}
+
+void decode_suite(char ext,
+                  gpr_timespec (*answer)(int64_t x, gpr_clock_type clock)) {
+  long test_vals[] = {1,       12,       123,       1234,     12345,   123456,
+                      1234567, 12345678, 123456789, 98765432, 9876543, 987654,
+                      98765,   9876,     987,       98,       9};
+  unsigned i;
+  char *input;
+  for (i = 0; i < GPR_ARRAY_SIZE(test_vals); i++) {
+    gpr_asprintf(&input, "%ld%c", test_vals[i], ext);
+    assert_decodes_as(input, answer(test_vals[i], GPR_TIMESPAN));
+    gpr_free(input);
+
+    gpr_asprintf(&input, "   %ld%c", test_vals[i], ext);
+    assert_decodes_as(input, answer(test_vals[i], GPR_TIMESPAN));
+    gpr_free(input);
+
+    gpr_asprintf(&input, "%ld %c", test_vals[i], ext);
+    assert_decodes_as(input, answer(test_vals[i], GPR_TIMESPAN));
+    gpr_free(input);
+
+    gpr_asprintf(&input, "%ld %c  ", test_vals[i], ext);
+    assert_decodes_as(input, answer(test_vals[i], GPR_TIMESPAN));
+    gpr_free(input);
+  }
+}
+
+void test_decoding(void) {
+  LOG_TEST("test_decoding");
+  decode_suite('n', gpr_time_from_nanos);
+  decode_suite('u', gpr_time_from_micros);
+  decode_suite('m', gpr_time_from_millis);
+  decode_suite('S', gpr_time_from_seconds);
+  decode_suite('M', gpr_time_from_minutes);
+  decode_suite('H', gpr_time_from_hours);
+  assert_decodes_as("1000000000S",
+                    gpr_time_from_seconds(1000 * 1000 * 1000, GPR_TIMESPAN));
+  assert_decodes_as("1000000000000000000000u", gpr_inf_future(GPR_TIMESPAN));
+  assert_decodes_as("1000000001S", gpr_inf_future(GPR_TIMESPAN));
+  assert_decodes_as("2000000001S", gpr_inf_future(GPR_TIMESPAN));
+  assert_decodes_as("9999999999S", gpr_inf_future(GPR_TIMESPAN));
+}
+
+void test_decoding_fails(void) {
+  gpr_timespec x;
+  LOG_TEST("test_decoding_fails");
+  GPR_ASSERT(0 == grpc_http2_decode_timeout("", &x));
+  GPR_ASSERT(0 == grpc_http2_decode_timeout(" ", &x));
+  GPR_ASSERT(0 == grpc_http2_decode_timeout("x", &x));
+  GPR_ASSERT(0 == grpc_http2_decode_timeout("1", &x));
+  GPR_ASSERT(0 == grpc_http2_decode_timeout("1x", &x));
+  GPR_ASSERT(0 == grpc_http2_decode_timeout("1ux", &x));
+  GPR_ASSERT(0 == grpc_http2_decode_timeout("!", &x));
+  GPR_ASSERT(0 == grpc_http2_decode_timeout("n1", &x));
+  GPR_ASSERT(0 == grpc_http2_decode_timeout("-1u", &x));
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  test_encoding();
+  test_decoding();
+  test_decoding_fails();
+  return 0;
+}
diff --git a/test/core/util/test_config.c b/test/core/util/test_config.c
index 270d166..479aeda 100644
--- a/test/core/util/test_config.c
+++ b/test/core/util/test_config.c
@@ -34,7 +34,6 @@
 #include "test/core/util/test_config.h"
 
 #include <grpc/support/log.h>
-#include <grpc/support/port_platform.h>
 #include <signal.h>
 #include <stdbool.h>
 #include <stdio.h>
diff --git a/test/cpp/codegen/codegen_test_full.cc b/test/cpp/codegen/codegen_test_full.cc
index 4500540..d6e2416 100644
--- a/test/cpp/codegen/codegen_test_full.cc
+++ b/test/cpp/codegen/codegen_test_full.cc
@@ -32,6 +32,7 @@
  */
 
 #include <grpc++/completion_queue.h>
+#include <grpc/support/time.h>
 #include <gtest/gtest.h>
 
 namespace grpc {
diff --git a/test/cpp/codegen/compiler_test_golden b/test/cpp/codegen/compiler_test_golden
index ef3d1aa..7b0fd6c 100644
--- a/test/cpp/codegen/compiler_test_golden
+++ b/test/cpp/codegen/compiler_test_golden
@@ -43,6 +43,7 @@
 
 #include <grpc++/impl/codegen/async_stream.h>
 #include <grpc++/impl/codegen/async_unary_call.h>
+#include <grpc++/impl/codegen/method_handler_impl.h>
 #include <grpc++/impl/codegen/proto_utils.h>
 #include <grpc++/impl/codegen/rpc_method.h>
 #include <grpc++/impl/codegen/service_type.h>
@@ -206,6 +207,27 @@
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
   };
+  template <class BaseClass>
+  class WithStreamedUnaryMethod_MethodA1 : public BaseClass {
+   private:
+    void BaseClassMustBeDerivedFromService(const Service *service) {}
+   public:
+    WithStreamedUnaryMethod_MethodA1() {
+      ::grpc::Service::MarkMethodStreamedUnary(0,
+        new ::grpc::StreamedUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>(std::bind(&WithStreamedUnaryMethod_MethodA1<BaseClass>::StreamedMethodA1, this, std::placeholders::_1, std::placeholders::_2)));
+    }
+    ~WithStreamedUnaryMethod_MethodA1() GRPC_OVERRIDE {
+      BaseClassMustBeDerivedFromService(this);
+    }
+    // disable regular version of this method
+    ::grpc::Status MethodA1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) GRPC_FINAL GRPC_OVERRIDE {
+      abort();
+      return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+    }
+    // replace default version of method with streamed unary
+    virtual ::grpc::Status StreamedMethodA1(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::grpc::testing::Request,::grpc::testing::Response>* server_unary_streamer) = 0;
+  };
+  typedef WithStreamedUnaryMethod_MethodA1<Service > StreamedUnaryService;
 };
 
 // ServiceB leading comment 1
@@ -284,6 +306,27 @@
       return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
     }
   };
+  template <class BaseClass>
+  class WithStreamedUnaryMethod_MethodB1 : public BaseClass {
+   private:
+    void BaseClassMustBeDerivedFromService(const Service *service) {}
+   public:
+    WithStreamedUnaryMethod_MethodB1() {
+      ::grpc::Service::MarkMethodStreamedUnary(0,
+        new ::grpc::StreamedUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>(std::bind(&WithStreamedUnaryMethod_MethodB1<BaseClass>::StreamedMethodB1, this, std::placeholders::_1, std::placeholders::_2)));
+    }
+    ~WithStreamedUnaryMethod_MethodB1() GRPC_OVERRIDE {
+      BaseClassMustBeDerivedFromService(this);
+    }
+    // disable regular version of this method
+    ::grpc::Status MethodB1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) GRPC_FINAL GRPC_OVERRIDE {
+      abort();
+      return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
+    }
+    // replace default version of method with streamed unary
+    virtual ::grpc::Status StreamedMethodB1(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::grpc::testing::Request,::grpc::testing::Response>* server_unary_streamer) = 0;
+  };
+  typedef WithStreamedUnaryMethod_MethodB1<Service > StreamedUnaryService;
 };
 // ServiceB trailing comment 1
 
diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc
index ac79fe8..a9b7c8f 100644
--- a/test/cpp/end2end/async_end2end_test.cc
+++ b/test/cpp/end2end/async_end2end_test.cc
@@ -897,8 +897,8 @@
       GetChannelCredentials(GetParam().credentials_type, &args);
   std::shared_ptr<Channel> channel =
       CreateCustomChannel(server_address_.str(), channel_creds, args);
-  std::unique_ptr<grpc::testing::UnimplementedService::Stub> stub;
-  stub = grpc::testing::UnimplementedService::NewStub(channel);
+  std::unique_ptr<grpc::testing::UnimplementedEchoService::Stub> stub;
+  stub = grpc::testing::UnimplementedEchoService::NewStub(channel);
   EchoRequest send_request;
   EchoResponse recv_response;
   Status recv_status;
diff --git a/test/cpp/end2end/client_crash_test.cc b/test/cpp/end2end/client_crash_test.cc
index 30b04fb..c452ad2 100644
--- a/test/cpp/end2end/client_crash_test.cc
+++ b/test/cpp/end2end/client_crash_test.cc
@@ -38,6 +38,7 @@
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
 #include <grpc/grpc.h>
+#include <grpc/support/log.h>
 #include <grpc/support/thd.h>
 #include <grpc/support/time.h>
 #include <gtest/gtest.h>
diff --git a/test/cpp/end2end/client_crash_test_server.cc b/test/cpp/end2end/client_crash_test_server.cc
index 9568ca2..6e14574 100644
--- a/test/cpp/end2end/client_crash_test_server.cc
+++ b/test/cpp/end2end/client_crash_test_server.cc
@@ -39,6 +39,8 @@
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
+#include <grpc/support/log.h>
+
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
 
 DEFINE_string(address, "", "Address to bind to");
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 46a58d3..b1d3ce9 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -44,6 +44,7 @@
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
 #include <grpc/grpc.h>
+#include <grpc/support/log.h>
 #include <grpc/support/thd.h>
 #include <grpc/support/time.h>
 #include <gtest/gtest.h>
@@ -79,11 +80,14 @@
 
 class TestMetadataCredentialsPlugin : public MetadataCredentialsPlugin {
  public:
-  static const char kMetadataKey[];
+  static const char kGoodMetadataKey[];
+  static const char kBadMetadataKey[];
 
-  TestMetadataCredentialsPlugin(grpc::string_ref metadata_value,
+  TestMetadataCredentialsPlugin(grpc::string_ref metadata_key,
+                                grpc::string_ref metadata_value,
                                 bool is_blocking, bool is_successful)
-      : metadata_value_(metadata_value.data(), metadata_value.length()),
+      : metadata_key_(metadata_key.data(), metadata_key.length()),
+        metadata_value_(metadata_value.data(), metadata_value.length()),
         is_blocking_(is_blocking),
         is_successful_(is_successful) {}
 
@@ -98,7 +102,7 @@
     EXPECT_TRUE(channel_auth_context.IsPeerAuthenticated());
     EXPECT_TRUE(metadata != nullptr);
     if (is_successful_) {
-      metadata->insert(std::make_pair(kMetadataKey, metadata_value_));
+      metadata->insert(std::make_pair(metadata_key_, metadata_value_));
       return Status::OK;
     } else {
       return Status(StatusCode::NOT_FOUND, kTestCredsPluginErrorMsg);
@@ -106,12 +110,16 @@
   }
 
  private:
+  grpc::string metadata_key_;
   grpc::string metadata_value_;
   bool is_blocking_;
   bool is_successful_;
 };
 
-const char TestMetadataCredentialsPlugin::kMetadataKey[] = "TestPluginMetadata";
+const char TestMetadataCredentialsPlugin::kBadMetadataKey[] =
+    "TestPluginMetadata";
+const char TestMetadataCredentialsPlugin::kGoodMetadataKey[] =
+    "test-plugin-metadata";
 
 class TestAuthMetadataProcessor : public AuthMetadataProcessor {
  public:
@@ -122,13 +130,17 @@
   std::shared_ptr<CallCredentials> GetCompatibleClientCreds() {
     return MetadataCredentialsFromPlugin(
         std::unique_ptr<MetadataCredentialsPlugin>(
-            new TestMetadataCredentialsPlugin(kGoodGuy, is_blocking_, true)));
+            new TestMetadataCredentialsPlugin(
+                TestMetadataCredentialsPlugin::kGoodMetadataKey, kGoodGuy,
+                is_blocking_, true)));
   }
 
   std::shared_ptr<CallCredentials> GetIncompatibleClientCreds() {
     return MetadataCredentialsFromPlugin(
         std::unique_ptr<MetadataCredentialsPlugin>(
-            new TestMetadataCredentialsPlugin("Mr Hyde", is_blocking_, true)));
+            new TestMetadataCredentialsPlugin(
+                TestMetadataCredentialsPlugin::kGoodMetadataKey, "Mr Hyde",
+                is_blocking_, true)));
   }
 
   // Interface implementation
@@ -141,7 +153,7 @@
     EXPECT_TRUE(context != nullptr);
     EXPECT_TRUE(response_metadata != nullptr);
     auto auth_md =
-        auth_metadata.find(TestMetadataCredentialsPlugin::kMetadataKey);
+        auth_metadata.find(TestMetadataCredentialsPlugin::kGoodMetadataKey);
     EXPECT_NE(auth_md, auth_metadata.end());
     string_ref auth_md_value = auth_md->second;
     if (auth_md_value == kGoodGuy) {
@@ -967,8 +979,8 @@
 // Talking to a non-existing service.
 TEST_P(End2endTest, NonExistingService) {
   ResetChannel();
-  std::unique_ptr<grpc::testing::UnimplementedService::Stub> stub;
-  stub = grpc::testing::UnimplementedService::NewStub(channel_);
+  std::unique_ptr<grpc::testing::UnimplementedEchoService::Stub> stub;
+  stub = grpc::testing::UnimplementedEchoService::NewStub(channel_);
 
   EchoRequest request;
   EchoResponse response;
@@ -1321,6 +1333,40 @@
   EXPECT_TRUE(s.ok());
 }
 
+TEST_P(SecureEnd2endTest, AuthMetadataPluginKeyFailure) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+  context.set_credentials(
+      MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin>(
+          new TestMetadataCredentialsPlugin(
+              TestMetadataCredentialsPlugin::kBadMetadataKey,
+              "Does not matter, will fail the key is invalid.", false, true))));
+  request.set_message("Hello");
+
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_FALSE(s.ok());
+  EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
+}
+
+TEST_P(SecureEnd2endTest, AuthMetadataPluginValueFailure) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+  context.set_credentials(
+      MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin>(
+          new TestMetadataCredentialsPlugin(
+              TestMetadataCredentialsPlugin::kGoodMetadataKey,
+              "With illegal \n value.", false, true))));
+  request.set_message("Hello");
+
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_FALSE(s.ok());
+  EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
+}
+
 TEST_P(SecureEnd2endTest, NonBlockingAuthMetadataPluginFailure) {
   ResetStub();
   EchoRequest request;
@@ -1329,6 +1375,7 @@
   context.set_credentials(
       MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin>(
           new TestMetadataCredentialsPlugin(
+              TestMetadataCredentialsPlugin::kGoodMetadataKey,
               "Does not matter, will fail anyway (see 3rd param)", false,
               false))));
   request.set_message("Hello");
@@ -1387,6 +1434,7 @@
   context.set_credentials(
       MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin>(
           new TestMetadataCredentialsPlugin(
+              TestMetadataCredentialsPlugin::kGoodMetadataKey,
               "Does not matter, will fail anyway (see 3rd param)", true,
               false))));
   request.set_message("Hello");
diff --git a/test/cpp/end2end/filter_end2end_test.cc b/test/cpp/end2end/filter_end2end_test.cc
new file mode 100644
index 0000000..853720f
--- /dev/null
+++ b/test/cpp/end2end/filter_end2end_test.cc
@@ -0,0 +1,353 @@
+/*
+ *
+ * Copyright 2016, 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 <memory>
+#include <mutex>
+
+#include <grpc++/channel.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/generic/async_generic_service.h>
+#include <grpc++/generic/generic_stub.h>
+#include <grpc++/impl/codegen/proto_utils.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_context.h>
+#include <grpc++/support/config.h>
+#include <grpc++/support/slice.h>
+#include <grpc/grpc.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include <gtest/gtest.h>
+
+#include "src/cpp/common/channel_filter.h"
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+#include "test/cpp/util/byte_buffer_proto_helper.h"
+
+using grpc::testing::EchoRequest;
+using grpc::testing::EchoResponse;
+using std::chrono::system_clock;
+
+namespace grpc {
+namespace testing {
+namespace {
+
+void* tag(int i) { return (void*)(intptr_t)i; }
+
+void verify_ok(CompletionQueue* cq, int i, bool expect_ok) {
+  bool ok;
+  void* got_tag;
+  EXPECT_TRUE(cq->Next(&got_tag, &ok));
+  EXPECT_EQ(expect_ok, ok);
+  EXPECT_EQ(tag(i), got_tag);
+}
+
+namespace {
+
+int global_num_connections = 0;
+int global_num_calls = 0;
+mutex global_mu;
+
+void IncrementConnectionCounter() {
+  unique_lock<mutex> lock(global_mu);
+  ++global_num_connections;
+}
+
+void ResetConnectionCounter() {
+  unique_lock<mutex> lock(global_mu);
+  global_num_connections = 0;
+}
+
+int GetConnectionCounterValue() {
+  unique_lock<mutex> lock(global_mu);
+  return global_num_connections;
+}
+
+void IncrementCallCounter() {
+  unique_lock<mutex> lock(global_mu);
+  ++global_num_calls;
+}
+
+void ResetCallCounter() {
+  unique_lock<mutex> lock(global_mu);
+  global_num_calls = 0;
+}
+
+int GetCallCounterValue() {
+  unique_lock<mutex> lock(global_mu);
+  return global_num_calls;
+}
+
+}  // namespace
+
+class ChannelDataImpl : public ChannelData {
+ public:
+  ChannelDataImpl(const grpc_channel_args& args, const char* peer)
+      : ChannelData(args, peer) {
+    IncrementConnectionCounter();
+  }
+};
+
+class CallDataImpl : public CallData {
+ public:
+  explicit CallDataImpl(const ChannelDataImpl& channel_data)
+      : CallData(channel_data) {}
+
+  void StartTransportStreamOp(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
+                              TransportStreamOp* op) GRPC_OVERRIDE {
+    // Incrementing the counter could be done from the ctor, but we want
+    // to test that the individual methods are actually called correctly.
+    if (op->recv_initial_metadata() != nullptr) IncrementCallCounter();
+    grpc_call_next_op(exec_ctx, elem, op->op());
+  }
+};
+
+class FilterEnd2endTest : public ::testing::Test {
+ protected:
+  FilterEnd2endTest() : server_host_("localhost") {}
+
+  void SetUp() GRPC_OVERRIDE {
+    int port = grpc_pick_unused_port_or_die();
+    server_address_ << server_host_ << ":" << port;
+    // Setup server
+    ServerBuilder builder;
+    builder.AddListeningPort(server_address_.str(),
+                             InsecureServerCredentials());
+    builder.RegisterAsyncGenericService(&generic_service_);
+    srv_cq_ = builder.AddCompletionQueue();
+    server_ = builder.BuildAndStart();
+  }
+
+  void TearDown() GRPC_OVERRIDE {
+    server_->Shutdown();
+    void* ignored_tag;
+    bool ignored_ok;
+    cli_cq_.Shutdown();
+    srv_cq_->Shutdown();
+    while (cli_cq_.Next(&ignored_tag, &ignored_ok))
+      ;
+    while (srv_cq_->Next(&ignored_tag, &ignored_ok))
+      ;
+  }
+
+  void ResetStub() {
+    std::shared_ptr<Channel> channel =
+        CreateChannel(server_address_.str(), InsecureChannelCredentials());
+    generic_stub_.reset(new GenericStub(channel));
+    ResetConnectionCounter();
+    ResetCallCounter();
+  }
+
+  void server_ok(int i) { verify_ok(srv_cq_.get(), i, true); }
+  void client_ok(int i) { verify_ok(&cli_cq_, i, true); }
+  void server_fail(int i) { verify_ok(srv_cq_.get(), i, false); }
+  void client_fail(int i) { verify_ok(&cli_cq_, i, false); }
+
+  void SendRpc(int num_rpcs) {
+    const grpc::string kMethodName("/grpc.cpp.test.util.EchoTestService/Echo");
+    for (int i = 0; i < num_rpcs; i++) {
+      EchoRequest send_request;
+      EchoRequest recv_request;
+      EchoResponse send_response;
+      EchoResponse recv_response;
+      Status recv_status;
+
+      ClientContext cli_ctx;
+      GenericServerContext srv_ctx;
+      GenericServerAsyncReaderWriter stream(&srv_ctx);
+
+      // The string needs to be long enough to test heap-based slice.
+      send_request.set_message("Hello world. Hello world. Hello world.");
+      std::unique_ptr<GenericClientAsyncReaderWriter> call =
+          generic_stub_->Call(&cli_ctx, kMethodName, &cli_cq_, tag(1));
+      client_ok(1);
+      std::unique_ptr<ByteBuffer> send_buffer =
+          SerializeToByteBuffer(&send_request);
+      call->Write(*send_buffer, tag(2));
+      // Send ByteBuffer can be destroyed after calling Write.
+      send_buffer.reset();
+      client_ok(2);
+      call->WritesDone(tag(3));
+      client_ok(3);
+
+      generic_service_.RequestCall(&srv_ctx, &stream, srv_cq_.get(),
+                                   srv_cq_.get(), tag(4));
+
+      verify_ok(srv_cq_.get(), 4, true);
+      EXPECT_EQ(server_host_, srv_ctx.host().substr(0, server_host_.length()));
+      EXPECT_EQ(kMethodName, srv_ctx.method());
+      ByteBuffer recv_buffer;
+      stream.Read(&recv_buffer, tag(5));
+      server_ok(5);
+      EXPECT_TRUE(ParseFromByteBuffer(&recv_buffer, &recv_request));
+      EXPECT_EQ(send_request.message(), recv_request.message());
+
+      send_response.set_message(recv_request.message());
+      send_buffer = SerializeToByteBuffer(&send_response);
+      stream.Write(*send_buffer, tag(6));
+      send_buffer.reset();
+      server_ok(6);
+
+      stream.Finish(Status::OK, tag(7));
+      server_ok(7);
+
+      recv_buffer.Clear();
+      call->Read(&recv_buffer, tag(8));
+      client_ok(8);
+      EXPECT_TRUE(ParseFromByteBuffer(&recv_buffer, &recv_response));
+
+      call->Finish(&recv_status, tag(9));
+      client_ok(9);
+
+      EXPECT_EQ(send_response.message(), recv_response.message());
+      EXPECT_TRUE(recv_status.ok());
+    }
+  }
+
+  CompletionQueue cli_cq_;
+  std::unique_ptr<ServerCompletionQueue> srv_cq_;
+  std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
+  std::unique_ptr<grpc::GenericStub> generic_stub_;
+  std::unique_ptr<Server> server_;
+  AsyncGenericService generic_service_;
+  const grpc::string server_host_;
+  std::ostringstream server_address_;
+};
+
+TEST_F(FilterEnd2endTest, SimpleRpc) {
+  ResetStub();
+  EXPECT_EQ(0, GetConnectionCounterValue());
+  EXPECT_EQ(0, GetCallCounterValue());
+  SendRpc(1);
+  EXPECT_EQ(1, GetConnectionCounterValue());
+  EXPECT_EQ(1, GetCallCounterValue());
+}
+
+TEST_F(FilterEnd2endTest, SequentialRpcs) {
+  ResetStub();
+  EXPECT_EQ(0, GetConnectionCounterValue());
+  EXPECT_EQ(0, GetCallCounterValue());
+  SendRpc(10);
+  EXPECT_EQ(1, GetConnectionCounterValue());
+  EXPECT_EQ(10, GetCallCounterValue());
+}
+
+// One ping, one pong.
+TEST_F(FilterEnd2endTest, SimpleBidiStreaming) {
+  ResetStub();
+  EXPECT_EQ(0, GetConnectionCounterValue());
+  EXPECT_EQ(0, GetCallCounterValue());
+
+  const grpc::string kMethodName(
+      "/grpc.cpp.test.util.EchoTestService/BidiStream");
+  EchoRequest send_request;
+  EchoRequest recv_request;
+  EchoResponse send_response;
+  EchoResponse recv_response;
+  Status recv_status;
+  ClientContext cli_ctx;
+  GenericServerContext srv_ctx;
+  GenericServerAsyncReaderWriter srv_stream(&srv_ctx);
+
+  cli_ctx.set_compression_algorithm(GRPC_COMPRESS_GZIP);
+  send_request.set_message("Hello");
+  std::unique_ptr<GenericClientAsyncReaderWriter> cli_stream =
+      generic_stub_->Call(&cli_ctx, kMethodName, &cli_cq_, tag(1));
+  client_ok(1);
+
+  generic_service_.RequestCall(&srv_ctx, &srv_stream, srv_cq_.get(),
+                               srv_cq_.get(), tag(2));
+
+  verify_ok(srv_cq_.get(), 2, true);
+  EXPECT_EQ(server_host_, srv_ctx.host().substr(0, server_host_.length()));
+  EXPECT_EQ(kMethodName, srv_ctx.method());
+
+  std::unique_ptr<ByteBuffer> send_buffer =
+      SerializeToByteBuffer(&send_request);
+  cli_stream->Write(*send_buffer, tag(3));
+  send_buffer.reset();
+  client_ok(3);
+
+  ByteBuffer recv_buffer;
+  srv_stream.Read(&recv_buffer, tag(4));
+  server_ok(4);
+  EXPECT_TRUE(ParseFromByteBuffer(&recv_buffer, &recv_request));
+  EXPECT_EQ(send_request.message(), recv_request.message());
+
+  send_response.set_message(recv_request.message());
+  send_buffer = SerializeToByteBuffer(&send_response);
+  srv_stream.Write(*send_buffer, tag(5));
+  send_buffer.reset();
+  server_ok(5);
+
+  cli_stream->Read(&recv_buffer, tag(6));
+  client_ok(6);
+  EXPECT_TRUE(ParseFromByteBuffer(&recv_buffer, &recv_response));
+  EXPECT_EQ(send_response.message(), recv_response.message());
+
+  cli_stream->WritesDone(tag(7));
+  client_ok(7);
+
+  srv_stream.Read(&recv_buffer, tag(8));
+  server_fail(8);
+
+  srv_stream.Finish(Status::OK, tag(9));
+  server_ok(9);
+
+  cli_stream->Finish(&recv_status, tag(10));
+  client_ok(10);
+
+  EXPECT_EQ(send_response.message(), recv_response.message());
+  EXPECT_TRUE(recv_status.ok());
+
+  EXPECT_EQ(1, GetCallCounterValue());
+  EXPECT_EQ(1, GetConnectionCounterValue());
+}
+
+void RegisterFilter() {
+  grpc::RegisterChannelFilter<ChannelDataImpl, CallDataImpl>(
+      "test-filter", GRPC_SERVER_CHANNEL, INT_MAX, nullptr);
+}
+
+}  // namespace
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  grpc_test_init(argc, argv);
+  ::testing::InitGoogleTest(&argc, argv);
+  grpc::testing::RegisterFilter();
+  return RUN_ALL_TESTS();
+}
diff --git a/test/cpp/end2end/hybrid_end2end_test.cc b/test/cpp/end2end/hybrid_end2end_test.cc
index 7e0c0e8..82361d0 100644
--- a/test/cpp/end2end/hybrid_end2end_test.cc
+++ b/test/cpp/end2end/hybrid_end2end_test.cc
@@ -199,7 +199,8 @@
   HybridEnd2endTest() {}
 
   void SetUpServer(::grpc::Service* service1, ::grpc::Service* service2,
-                   AsyncGenericService* generic_service) {
+                   AsyncGenericService* generic_service,
+                   int max_message_size = 0) {
     int port = grpc_pick_unused_port_or_die();
     server_address_ << "localhost:" << port;
 
@@ -217,6 +218,11 @@
     if (generic_service) {
       builder.RegisterAsyncGenericService(generic_service);
     }
+
+    if (max_message_size != 0) {
+      builder.SetMaxMessageSize(max_message_size);
+    }
+
     // Create a separate cq for each potential handler.
     for (int i = 0; i < 5; i++) {
       cqs_.push_back(builder.AddCompletionQueue(false));
@@ -346,7 +352,7 @@
     EXPECT_TRUE(s.ok());
   }
 
-  grpc::testing::UnimplementedService::Service unimplemented_service_;
+  grpc::testing::UnimplementedEchoService::Service unimplemented_service_;
   std::vector<std::unique_ptr<ServerCompletionQueue>> cqs_;
   std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
   std::unique_ptr<Server> server_;
@@ -415,6 +421,83 @@
   request_stream_handler_thread.join();
 }
 
+// Add a second service with one sync streamed unary method.
+class StreamedUnaryDupPkg
+    : public duplicate::EchoTestService::WithStreamedUnaryMethod_Echo<
+          TestServiceImplDupPkg> {
+ public:
+  Status StreamedEcho(ServerContext* context,
+                      ServerUnaryStreamer<EchoRequest, EchoResponse>* stream)
+      GRPC_OVERRIDE {
+    EchoRequest req;
+    EchoResponse resp;
+    uint32_t next_msg_sz;
+    stream->NextMessageSize(&next_msg_sz);
+    gpr_log(GPR_INFO, "Streamed Unary Next Message Size is %u", next_msg_sz);
+    GPR_ASSERT(stream->Read(&req));
+    resp.set_message(req.message() + "_dup");
+    GPR_ASSERT(stream->Write(resp));
+    return Status::OK;
+  }
+};
+
+TEST_F(HybridEnd2endTest,
+       AsyncRequestStreamResponseStream_SyncStreamedUnaryDupService) {
+  typedef EchoTestService::WithAsyncMethod_RequestStream<
+      EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl>>
+      SType;
+  SType service;
+  StreamedUnaryDupPkg dup_service;
+  SetUpServer(&service, &dup_service, nullptr, 8192);
+  ResetStub();
+  std::thread response_stream_handler_thread(HandleServerStreaming<SType>,
+                                             &service, cqs_[0].get());
+  std::thread request_stream_handler_thread(HandleClientStreaming<SType>,
+                                            &service, cqs_[1].get());
+  TestAllMethods();
+  SendEchoToDupService();
+  response_stream_handler_thread.join();
+  request_stream_handler_thread.join();
+}
+
+// Add a second service that is fully Streamed Unary
+class FullyStreamedUnaryDupPkg
+    : public duplicate::EchoTestService::StreamedUnaryService {
+ public:
+  Status StreamedEcho(ServerContext* context,
+                      ServerUnaryStreamer<EchoRequest, EchoResponse>* stream)
+      GRPC_OVERRIDE {
+    EchoRequest req;
+    EchoResponse resp;
+    uint32_t next_msg_sz;
+    stream->NextMessageSize(&next_msg_sz);
+    gpr_log(GPR_INFO, "Streamed Unary Next Message Size is %u", next_msg_sz);
+    GPR_ASSERT(stream->Read(&req));
+    resp.set_message(req.message() + "_dup");
+    GPR_ASSERT(stream->Write(resp));
+    return Status::OK;
+  }
+};
+
+TEST_F(HybridEnd2endTest,
+       AsyncRequestStreamResponseStream_SyncFullyStreamedUnaryDupService) {
+  typedef EchoTestService::WithAsyncMethod_RequestStream<
+      EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl>>
+      SType;
+  SType service;
+  FullyStreamedUnaryDupPkg dup_service;
+  SetUpServer(&service, &dup_service, nullptr, 8192);
+  ResetStub();
+  std::thread response_stream_handler_thread(HandleServerStreaming<SType>,
+                                             &service, cqs_[0].get());
+  std::thread request_stream_handler_thread(HandleClientStreaming<SType>,
+                                            &service, cqs_[1].get());
+  TestAllMethods();
+  SendEchoToDupService();
+  response_stream_handler_thread.join();
+  request_stream_handler_thread.join();
+}
+
 // Add a second service with one async method.
 TEST_F(HybridEnd2endTest, AsyncRequestStreamResponseStream_AsyncDupService) {
   typedef EchoTestService::WithAsyncMethod_RequestStream<
diff --git a/test/cpp/end2end/mock_test.cc b/test/cpp/end2end/mock_test.cc
index 0ace5d9..0da5861 100644
--- a/test/cpp/end2end/mock_test.cc
+++ b/test/cpp/end2end/mock_test.cc
@@ -31,6 +31,7 @@
  *
  */
 
+#include <climits>
 #include <thread>
 
 #include <grpc++/channel.h>
@@ -40,6 +41,7 @@
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
 #include <grpc/grpc.h>
+#include <grpc/support/log.h>
 #include <grpc/support/thd.h>
 #include <grpc/support/time.h>
 #include <gtest/gtest.h>
@@ -63,6 +65,10 @@
     : public ClientReaderWriterInterface<W, R> {
  public:
   void WaitForInitialMetadata() GRPC_OVERRIDE {}
+  bool NextMessageSize(uint32_t* sz) GRPC_OVERRIDE {
+    *sz = UINT_MAX;
+    return true;
+  }
   bool Read(R* msg) GRPC_OVERRIDE { return true; }
   bool Write(const W& msg) GRPC_OVERRIDE { return true; }
   bool WritesDone() GRPC_OVERRIDE { return true; }
@@ -74,6 +80,10 @@
  public:
   MockClientReaderWriter() : writes_done_(false) {}
   void WaitForInitialMetadata() GRPC_OVERRIDE {}
+  bool NextMessageSize(uint32_t* sz) GRPC_OVERRIDE {
+    *sz = UINT_MAX;
+    return true;
+  }
   bool Read(EchoResponse* msg) GRPC_OVERRIDE {
     if (writes_done_) return false;
     msg->set_message(last_message_);
diff --git a/test/cpp/end2end/server_crash_test.cc b/test/cpp/end2end/server_crash_test.cc
index e447360..16a5fa2 100644
--- a/test/cpp/end2end/server_crash_test.cc
+++ b/test/cpp/end2end/server_crash_test.cc
@@ -38,6 +38,7 @@
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
 #include <grpc/grpc.h>
+#include <grpc/support/log.h>
 #include <grpc/support/thd.h>
 #include <grpc/support/time.h>
 #include <gtest/gtest.h>
diff --git a/test/cpp/end2end/server_crash_test_client.cc b/test/cpp/end2end/server_crash_test_client.cc
index ece948d..10a251c 100644
--- a/test/cpp/end2end/server_crash_test_client.cc
+++ b/test/cpp/end2end/server_crash_test_client.cc
@@ -40,6 +40,8 @@
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
+#include <grpc/support/log.h>
+
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
 
 DEFINE_string(address, "", "Address to connect to");
diff --git a/test/cpp/end2end/shutdown_test.cc b/test/cpp/end2end/shutdown_test.cc
index 3f98de6..4cba3b1 100644
--- a/test/cpp/end2end/shutdown_test.cc
+++ b/test/cpp/end2end/shutdown_test.cc
@@ -33,6 +33,8 @@
 
 #include <thread>
 
+#include <gtest/gtest.h>
+
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
@@ -40,8 +42,8 @@
 #include <grpc++/server_builder.h>
 #include <grpc++/server_context.h>
 #include <grpc/grpc.h>
+#include <grpc/support/log.h>
 #include <grpc/support/sync.h>
-#include <gtest/gtest.h>
 
 #include "src/core/lib/support/env.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
diff --git a/test/cpp/end2end/streaming_throughput_test.cc b/test/cpp/end2end/streaming_throughput_test.cc
index 9e82c30..fbef761 100644
--- a/test/cpp/end2end/streaming_throughput_test.cc
+++ b/test/cpp/end2end/streaming_throughput_test.cc
@@ -35,6 +35,8 @@
 #include <mutex>
 #include <thread>
 
+#include <gtest/gtest.h>
+
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
@@ -45,9 +47,9 @@
 #include <grpc++/server_context.h>
 #include <grpc/grpc.h>
 #include <grpc/support/atm.h>
+#include <grpc/support/log.h>
 #include <grpc/support/thd.h>
 #include <grpc/support/time.h>
-#include <gtest/gtest.h>
 
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
diff --git a/test/cpp/end2end/test_service_impl.cc b/test/cpp/end2end/test_service_impl.cc
index 52abd80..2de344e 100644
--- a/test/cpp/end2end/test_service_impl.cc
+++ b/test/cpp/end2end/test_service_impl.cc
@@ -38,7 +38,8 @@
 
 #include <grpc++/security/credentials.h>
 #include <grpc++/server_context.h>
-#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+
 #include <gtest/gtest.h>
 
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
diff --git a/test/cpp/grpclb/grpclb_api_test.cc b/test/cpp/grpclb/grpclb_api_test.cc
index bf77878..e67189c 100644
--- a/test/cpp/grpclb/grpclb_api_test.cc
+++ b/test/cpp/grpclb/grpclb_api_test.cc
@@ -31,10 +31,12 @@
  *
  */
 
+#include <grpc++/impl/codegen/config.h>
 #include <gtest/gtest.h>
-#include <string>
 
 #include "src/core/ext/lb_policy/grpclb/load_balancer_api.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/proto/grpc/lb/v1/load_balancer.pb.h"  // C++ version
 
 namespace grpc {
@@ -45,8 +47,28 @@
 
 class GrpclbTest : public ::testing::Test {};
 
+grpc::string Ip4ToPackedString(const char* ip_str) {
+  struct in_addr ip4;
+  GPR_ASSERT(inet_pton(AF_INET, ip_str, &ip4) == 1);
+  return grpc::string(reinterpret_cast<const char*>(&ip4), sizeof(ip4));
+}
+
+grpc::string PackedStringToIp(const grpc_grpclb_ip_address& pb_ip) {
+  char ip_str[46] = {0};
+  int af = -1;
+  if (pb_ip.size == 4) {
+    af = AF_INET;
+  } else if (pb_ip.size == 16) {
+    af = AF_INET6;
+  } else {
+    abort();
+  }
+  GPR_ASSERT(inet_ntop(af, pb_ip.bytes, ip_str, 46) != NULL);
+  return ip_str;
+}
+
 TEST_F(GrpclbTest, CreateRequest) {
-  const std::string service_name = "AServiceName";
+  const grpc::string service_name = "AServiceName";
   LoadBalanceRequest request;
   grpc_grpclb_request* c_req = grpc_grpclb_request_create(service_name.c_str());
   gpr_slice slice = grpc_grpclb_request_encode(c_req);
@@ -58,55 +80,55 @@
   grpc_grpclb_request_destroy(c_req);
 }
 
-TEST_F(GrpclbTest, ParseResponse) {
+TEST_F(GrpclbTest, ParseInitialResponse) {
   LoadBalanceResponse response;
   auto* initial_response = response.mutable_initial_response();
   auto* client_stats_report_interval =
       initial_response->mutable_client_stats_report_interval();
   client_stats_report_interval->set_seconds(123);
   client_stats_report_interval->set_nanos(456);
-
-  const std::string encoded_response = response.SerializeAsString();
+  const grpc::string encoded_response = response.SerializeAsString();
   gpr_slice encoded_slice =
       gpr_slice_from_copied_string(encoded_response.c_str());
-  grpc_grpclb_response* c_response = grpc_grpclb_response_parse(encoded_slice);
-  EXPECT_TRUE(c_response->has_initial_response);
-  EXPECT_FALSE(c_response->initial_response.has_load_balancer_delegate);
-  EXPECT_EQ(c_response->initial_response.client_stats_report_interval.seconds,
-            123);
-  EXPECT_EQ(c_response->initial_response.client_stats_report_interval.nanos,
-            456);
+
+  grpc_grpclb_initial_response* c_initial_response =
+      grpc_grpclb_initial_response_parse(encoded_slice);
+  EXPECT_FALSE(c_initial_response->has_load_balancer_delegate);
+  EXPECT_EQ(c_initial_response->client_stats_report_interval.seconds, 123);
+  EXPECT_EQ(c_initial_response->client_stats_report_interval.nanos, 456);
   gpr_slice_unref(encoded_slice);
-  grpc_grpclb_response_destroy(c_response);
+  grpc_grpclb_initial_response_destroy(c_initial_response);
 }
 
 TEST_F(GrpclbTest, ParseResponseServerList) {
   LoadBalanceResponse response;
   auto* serverlist = response.mutable_server_list();
   auto* server = serverlist->add_servers();
-  server->set_ip_address("127.0.0.1");
+  server->set_ip_address(Ip4ToPackedString("127.0.0.1"));
   server->set_port(12345);
   server->set_drop_request(true);
   server = response.mutable_server_list()->add_servers();
-  server->set_ip_address("10.0.0.1");
+  server->set_ip_address(Ip4ToPackedString("10.0.0.1"));
   server->set_port(54321);
   server->set_drop_request(false);
   auto* expiration_interval = serverlist->mutable_expiration_interval();
   expiration_interval->set_seconds(888);
   expiration_interval->set_nanos(999);
 
-  const std::string encoded_response = response.SerializeAsString();
-  gpr_slice encoded_slice =
-      gpr_slice_from_copied_string(encoded_response.c_str());
+  const grpc::string encoded_response = response.SerializeAsString();
+  const gpr_slice encoded_slice = gpr_slice_from_copied_buffer(
+      encoded_response.data(), encoded_response.size());
   grpc_grpclb_serverlist* c_serverlist =
       grpc_grpclb_response_parse_serverlist(encoded_slice);
   ASSERT_EQ(c_serverlist->num_servers, 2ul);
   EXPECT_TRUE(c_serverlist->servers[0]->has_ip_address);
-  EXPECT_TRUE(strcmp(c_serverlist->servers[0]->ip_address, "127.0.0.1") == 0);
+  EXPECT_EQ(PackedStringToIp(c_serverlist->servers[0]->ip_address),
+            "127.0.0.1");
   EXPECT_EQ(c_serverlist->servers[0]->port, 12345);
   EXPECT_TRUE(c_serverlist->servers[0]->drop_request);
   EXPECT_TRUE(c_serverlist->servers[1]->has_ip_address);
-  EXPECT_TRUE(strcmp(c_serverlist->servers[1]->ip_address, "10.0.0.1") == 0);
+
+  EXPECT_EQ(PackedStringToIp(c_serverlist->servers[1]->ip_address), "10.0.0.1");
   EXPECT_EQ(c_serverlist->servers[1]->port, 54321);
   EXPECT_FALSE(c_serverlist->servers[1]->drop_request);
 
diff --git a/test/cpp/grpclb/grpclb_test.cc b/test/cpp/grpclb/grpclb_test.cc
new file mode 100644
index 0000000..7666c4e
--- /dev/null
+++ b/test/cpp/grpclb/grpclb_test.cc
@@ -0,0 +1,725 @@
+/*
+ *
+ * Copyright 2016, 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 <cinttypes>
+#include <cstdarg>
+#include <cstdint>
+#include <cstring>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <grpc/grpc.h>
+#include <grpc/impl/codegen/byte_buffer_reader.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+
+#include <grpc++/impl/codegen/config.h>
+extern "C" {
+#include "src/core/ext/client_config/client_channel.h"
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/support/tmpfile.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/server.h"
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/end2end/fake_resolver.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+}
+
+#include "src/proto/grpc/lb/v1/load_balancer.pb.h"
+
+#define NUM_BACKENDS 4
+#define PAYLOAD "hello you"
+
+// TODO(dgq): Other scenarios in need of testing:
+// - Send an empty serverlist update and verify that the client request blocks
+//   until a new serverlist with actual contents is available.
+// - Send identical serverlist update
+// - Send a serverlist with faulty ip:port addresses (port > 2^16, etc).
+// - Test reception of invalid serverlist
+// - Test pinging
+// - Test against a non-LB server. That server should return UNIMPLEMENTED and
+//   the call should fail.
+// - Random LB server closing the stream unexpectedly.
+// - Test using DNS-resolvable names (localhost?)
+
+namespace grpc {
+namespace {
+
+typedef struct client_fixture {
+  grpc_channel *client;
+  char *server_uri;
+  grpc_completion_queue *cq;
+} client_fixture;
+
+typedef struct server_fixture {
+  grpc_server *server;
+  grpc_call *server_call;
+  grpc_completion_queue *cq;
+  char *servers_hostport;
+  int port;
+  gpr_thd_id tid;
+  int num_calls_serviced;
+} server_fixture;
+
+typedef struct test_fixture {
+  server_fixture lb_server;
+  server_fixture lb_backends[NUM_BACKENDS];
+  client_fixture client;
+  int lb_server_update_delay_ms;
+} test_fixture;
+
+static void *tag(intptr_t t) { return (void *)t; }
+
+static gpr_slice build_response_payload_slice(
+    const char *host, int *ports, size_t nports,
+    int64_t expiration_interval_secs, int32_t expiration_interval_nanos) {
+  // server_list {
+  //   servers {
+  //     ip_address: <in_addr/6 bytes of an IP>
+  //     port: <16 bit uint>
+  //     load_balance_token: "token..."
+  //   }
+  //   ...
+  // }
+  grpc::lb::v1::LoadBalanceResponse response;
+  auto *serverlist = response.mutable_server_list();
+
+  if (expiration_interval_secs > 0 || expiration_interval_nanos > 0) {
+    auto *expiration_interval = serverlist->mutable_expiration_interval();
+    if (expiration_interval_secs > 0) {
+      expiration_interval->set_seconds(expiration_interval_secs);
+    }
+    if (expiration_interval_nanos > 0) {
+      expiration_interval->set_nanos(expiration_interval_nanos);
+    }
+  }
+  for (size_t i = 0; i < nports; i++) {
+    auto *server = serverlist->add_servers();
+    // TODO(dgq): test ipv6
+    struct in_addr ip4;
+    GPR_ASSERT(inet_pton(AF_INET, host, &ip4) == 1);
+    server->set_ip_address(
+        grpc::string(reinterpret_cast<const char *>(&ip4), sizeof(ip4)));
+    server->set_port(ports[i]);
+    // The following long long int cast is meant to work around the
+    // disfunctional implementation of std::to_string in gcc 4.4, which doesn't
+    // have a version for int but does have one for long long int.
+    string token_data = "token" + std::to_string((long long int)ports[i]);
+    token_data.resize(64, '-');
+    server->set_load_balance_token(token_data);
+  }
+  const grpc::string &enc_resp = response.SerializeAsString();
+  return gpr_slice_from_copied_buffer(enc_resp.data(), enc_resp.size());
+}
+
+static void drain_cq(grpc_completion_queue *cq) {
+  grpc_event ev;
+  do {
+    ev = grpc_completion_queue_next(cq, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5),
+                                    NULL);
+  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void sleep_ms(int delay_ms) {
+  gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+                               gpr_time_from_millis(delay_ms, GPR_TIMESPAN)));
+}
+
+static void start_lb_server(server_fixture *sf, int *ports, size_t nports,
+                            int update_delay_ms) {
+  grpc_call *s;
+  cq_verifier *cqv = cq_verifier_create(sf->cq);
+  grpc_op ops[6];
+  grpc_op *op;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_call_error error;
+  int was_cancelled = 2;
+  grpc_byte_buffer *request_payload_recv;
+  grpc_byte_buffer *response_payload;
+
+  memset(ops, 0, sizeof(ops));
+  grpc_metadata_array_init(&request_metadata_recv);
+  grpc_call_details_init(&call_details);
+
+  error = grpc_server_request_call(sf->server, &s, &call_details,
+                                   &request_metadata_recv, sf->cq, sf->cq,
+                                   tag(200));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  gpr_log(GPR_INFO, "LB Server[%s] up", sf->servers_hostport);
+  CQ_EXPECT_COMPLETION(cqv, tag(200), 1);
+  cq_verify(cqv);
+  gpr_log(GPR_INFO, "LB Server[%s] after tag 200", sf->servers_hostport);
+
+  // make sure we've received the initial metadata from the grpclb request.
+  GPR_ASSERT(request_metadata_recv.count > 0);
+  GPR_ASSERT(request_metadata_recv.metadata != NULL);
+
+  // receive request for backends
+  op = ops;
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message = &request_payload_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(202), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  CQ_EXPECT_COMPLETION(cqv, tag(202), 1);
+  cq_verify(cqv);
+  gpr_log(GPR_INFO, "LB Server[%s] after RECV_MSG", sf->servers_hostport);
+
+  // validate initial request.
+  grpc_byte_buffer_reader bbr;
+  grpc_byte_buffer_reader_init(&bbr, request_payload_recv);
+  gpr_slice request_payload_slice = grpc_byte_buffer_reader_readall(&bbr);
+  grpc::lb::v1::LoadBalanceRequest request;
+  request.ParseFromArray(GPR_SLICE_START_PTR(request_payload_slice),
+                         GPR_SLICE_LENGTH(request_payload_slice));
+  GPR_ASSERT(request.has_initial_request());
+  GPR_ASSERT(request.initial_request().name() == sf->servers_hostport);
+  gpr_slice_unref(request_payload_slice);
+  grpc_byte_buffer_reader_destroy(&bbr);
+  grpc_byte_buffer_destroy(request_payload_recv);
+
+  gpr_slice response_payload_slice;
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  op->data.recv_close_on_server.cancelled = &was_cancelled;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(201), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+  gpr_log(GPR_INFO, "LB Server[%s] after tag 201", sf->servers_hostport);
+
+  for (int i = 0; i < 2; i++) {
+    if (i == 0) {
+      // First half of the ports.
+      response_payload_slice =
+          build_response_payload_slice("127.0.0.1", ports, nports / 2, -1, -1);
+    } else {
+      // Second half of the ports.
+      sleep_ms(update_delay_ms);
+      response_payload_slice =
+          build_response_payload_slice("127.0.0.1", ports + (nports / 2),
+                                       (nports + 1) / 2 /* ceil */, -1, -1);
+    }
+
+    response_payload = grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+    op = ops;
+    op->op = GRPC_OP_SEND_MESSAGE;
+    op->data.send_message = response_payload;
+    op->flags = 0;
+    op->reserved = NULL;
+    op++;
+    error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(203), NULL);
+    GPR_ASSERT(GRPC_CALL_OK == error);
+    CQ_EXPECT_COMPLETION(cqv, tag(203), 1);
+    cq_verify(cqv);
+    gpr_log(GPR_INFO, "LB Server[%s] after SEND_MESSAGE, iter %d",
+            sf->servers_hostport, i);
+
+    grpc_byte_buffer_destroy(response_payload);
+    gpr_slice_unref(response_payload_slice);
+  }
+  gpr_log(GPR_INFO, "LB Server[%s] shutting down", sf->servers_hostport);
+
+  op = ops;
+  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+  op->data.send_status_from_server.trailing_metadata_count = 0;
+  op->data.send_status_from_server.status = GRPC_STATUS_OK;
+  op->data.send_status_from_server.status_details = "xyz";
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(204), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(201), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(204), 1);
+  cq_verify(cqv);
+  gpr_log(GPR_INFO, "LB Server[%s] after tag 204. All done. LB server out",
+          sf->servers_hostport);
+
+  grpc_call_destroy(s);
+
+  cq_verifier_destroy(cqv);
+
+  grpc_metadata_array_destroy(&request_metadata_recv);
+  grpc_call_details_destroy(&call_details);
+}
+
+static void start_backend_server(server_fixture *sf) {
+  grpc_call *s;
+  cq_verifier *cqv;
+  grpc_op ops[6];
+  grpc_op *op;
+  grpc_metadata_array request_metadata_recv;
+  grpc_call_details call_details;
+  grpc_call_error error;
+  int was_cancelled;
+  grpc_byte_buffer *request_payload_recv;
+  grpc_byte_buffer *response_payload;
+  grpc_event ev;
+
+  while (true) {
+    memset(ops, 0, sizeof(ops));
+    cqv = cq_verifier_create(sf->cq);
+    was_cancelled = 2;
+    grpc_metadata_array_init(&request_metadata_recv);
+    grpc_call_details_init(&call_details);
+
+    error = grpc_server_request_call(sf->server, &s, &call_details,
+                                     &request_metadata_recv, sf->cq, sf->cq,
+                                     tag(100));
+    GPR_ASSERT(GRPC_CALL_OK == error);
+    gpr_log(GPR_INFO, "Server[%s] up", sf->servers_hostport);
+    ev = grpc_completion_queue_next(sf->cq,
+                                    GRPC_TIMEOUT_SECONDS_TO_DEADLINE(60), NULL);
+    if (!ev.success) {
+      gpr_log(GPR_INFO, "Server[%s] being torn down", sf->servers_hostport);
+      cq_verifier_destroy(cqv);
+      grpc_metadata_array_destroy(&request_metadata_recv);
+      grpc_call_details_destroy(&call_details);
+      return;
+    }
+    GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
+
+    // The following long long int cast is meant to work around the
+    // disfunctional implementation of std::to_string in gcc 4.4, which doesn't
+    // have a version for int but does have one for long long int.
+    string expected_token = "token" + std::to_string((long long int)sf->port);
+    expected_token.resize(64, '-');
+    GPR_ASSERT(contains_metadata(&request_metadata_recv,
+                                 "load-reporting-initial",
+                                 expected_token.c_str()));
+
+    gpr_log(GPR_INFO, "Server[%s] after tag 100", sf->servers_hostport);
+
+    op = ops;
+    op->op = GRPC_OP_SEND_INITIAL_METADATA;
+    op->data.send_initial_metadata.count = 0;
+    op->flags = 0;
+    op->reserved = NULL;
+    op++;
+    op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+    op->data.recv_close_on_server.cancelled = &was_cancelled;
+    op->flags = 0;
+    op->reserved = NULL;
+    op++;
+    error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(101), NULL);
+    GPR_ASSERT(GRPC_CALL_OK == error);
+    gpr_log(GPR_INFO, "Server[%s] after tag 101", sf->servers_hostport);
+
+    bool exit = false;
+    gpr_slice response_payload_slice = gpr_slice_from_copied_string(PAYLOAD);
+    while (!exit) {
+      op = ops;
+      op->op = GRPC_OP_RECV_MESSAGE;
+      op->data.recv_message = &request_payload_recv;
+      op->flags = 0;
+      op->reserved = NULL;
+      op++;
+      error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
+      GPR_ASSERT(GRPC_CALL_OK == error);
+      ev = grpc_completion_queue_next(
+          sf->cq, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3), NULL);
+      if (ev.type == GRPC_OP_COMPLETE && ev.success) {
+        GPR_ASSERT(ev.tag = tag(102));
+        if (request_payload_recv == NULL) {
+          exit = true;
+          gpr_log(GPR_INFO,
+                  "Server[%s] recv \"close\" from client, exiting. Call #%d",
+                  sf->servers_hostport, sf->num_calls_serviced);
+        }
+      } else {
+        gpr_log(GPR_INFO, "Server[%s] forced to shutdown. Call #%d",
+                sf->servers_hostport, sf->num_calls_serviced);
+        exit = true;
+      }
+      gpr_log(GPR_INFO, "Server[%s] after tag 102. Call #%d",
+              sf->servers_hostport, sf->num_calls_serviced);
+
+      if (!exit) {
+        response_payload =
+            grpc_raw_byte_buffer_create(&response_payload_slice, 1);
+        op = ops;
+        op->op = GRPC_OP_SEND_MESSAGE;
+        op->data.send_message = response_payload;
+        op->flags = 0;
+        op->reserved = NULL;
+        op++;
+        error =
+            grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
+        GPR_ASSERT(GRPC_CALL_OK == error);
+        ev = grpc_completion_queue_next(
+            sf->cq, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3), NULL);
+        if (ev.type == GRPC_OP_COMPLETE && ev.success) {
+          GPR_ASSERT(ev.tag = tag(103));
+        } else {
+          gpr_log(GPR_INFO, "Server[%s] forced to shutdown. Call #%d",
+                  sf->servers_hostport, sf->num_calls_serviced);
+          exit = true;
+        }
+        gpr_log(GPR_INFO, "Server[%s] after tag 103. Call #%d",
+                sf->servers_hostport, sf->num_calls_serviced);
+        grpc_byte_buffer_destroy(response_payload);
+      }
+
+      grpc_byte_buffer_destroy(request_payload_recv);
+    }
+    ++sf->num_calls_serviced;
+
+    gpr_log(GPR_INFO, "Server[%s] OUT OF THE LOOP", sf->servers_hostport);
+    gpr_slice_unref(response_payload_slice);
+
+    op = ops;
+    op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+    op->data.send_status_from_server.trailing_metadata_count = 0;
+    op->data.send_status_from_server.status = GRPC_STATUS_OK;
+    op->data.send_status_from_server.status_details = "Backend server out a-ok";
+    op->flags = 0;
+    op->reserved = NULL;
+    op++;
+    error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(104), NULL);
+    GPR_ASSERT(GRPC_CALL_OK == error);
+
+    CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
+    CQ_EXPECT_COMPLETION(cqv, tag(104), 1);
+    cq_verify(cqv);
+    gpr_log(GPR_INFO, "Server[%s] DONE. After servicing %d calls",
+            sf->servers_hostport, sf->num_calls_serviced);
+
+    grpc_call_destroy(s);
+    cq_verifier_destroy(cqv);
+    grpc_metadata_array_destroy(&request_metadata_recv);
+    grpc_call_details_destroy(&call_details);
+  }
+}
+
+static void perform_request(client_fixture *cf) {
+  grpc_call *c;
+  cq_verifier *cqv = cq_verifier_create(cf->cq);
+  grpc_op ops[6];
+  grpc_op *op;
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  grpc_status_code status;
+  grpc_call_error error;
+  char *details = NULL;
+  size_t details_capacity = 0;
+  grpc_byte_buffer *request_payload;
+  grpc_byte_buffer *response_payload_recv;
+  int i;
+
+  memset(ops, 0, sizeof(ops));
+  gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
+
+  c = grpc_channel_create_call(cf->client, NULL, GRPC_PROPAGATE_DEFAULTS,
+                               cf->cq, "/foo", "foo.test.google.fr:1234",
+                               GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL);
+  gpr_log(GPR_INFO, "Call 0x%" PRIxPTR " created", (intptr_t)c);
+  GPR_ASSERT(c);
+  char *peer;
+
+  grpc_metadata_array_init(&initial_metadata_recv);
+  grpc_metadata_array_init(&trailing_metadata_recv);
+
+  op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
+  op->data.recv_status_on_client.status = &status;
+  op->data.recv_status_on_client.status_details = &details;
+  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  for (i = 0; i < 4; i++) {
+    request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+
+    op = ops;
+    op->op = GRPC_OP_SEND_MESSAGE;
+    op->data.send_message = request_payload;
+    op->flags = 0;
+    op->reserved = NULL;
+    op++;
+    op->op = GRPC_OP_RECV_MESSAGE;
+    op->data.recv_message = &response_payload_recv;
+    op->flags = 0;
+    op->reserved = NULL;
+    op++;
+    error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(2), NULL);
+    GPR_ASSERT(GRPC_CALL_OK == error);
+
+    CQ_EXPECT_COMPLETION(cqv, tag(2), 1);
+    cq_verify(cqv);
+    GPR_ASSERT(byte_buffer_eq_string(response_payload_recv, PAYLOAD));
+
+    grpc_byte_buffer_destroy(request_payload);
+    grpc_byte_buffer_destroy(response_payload_recv);
+  }
+
+  gpr_slice_unref(request_payload_slice);
+
+  op = ops;
+  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+  op->flags = 0;
+  op->reserved = NULL;
+  op++;
+  error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(3), NULL);
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
+  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+  CQ_EXPECT_COMPLETION(cqv, tag(3), 1);
+  cq_verify(cqv);
+  peer = grpc_call_get_peer(c);
+  gpr_log(GPR_INFO, "Client DONE WITH SERVER %s ", peer);
+  gpr_free(peer);
+
+  grpc_call_destroy(c);
+
+  cq_verify_empty_timeout(cqv, 1);
+  cq_verifier_destroy(cqv);
+
+  grpc_metadata_array_destroy(&initial_metadata_recv);
+  grpc_metadata_array_destroy(&trailing_metadata_recv);
+  gpr_free(details);
+}
+
+static void setup_client(const char *server_hostport, client_fixture *cf) {
+  cf->cq = grpc_completion_queue_create(NULL);
+  cf->server_uri = gpr_strdup(server_hostport);
+  cf->client = grpc_insecure_channel_create(cf->server_uri, NULL, NULL);
+}
+
+static void teardown_client(client_fixture *cf) {
+  grpc_completion_queue_shutdown(cf->cq);
+  drain_cq(cf->cq);
+  grpc_completion_queue_destroy(cf->cq);
+  cf->cq = NULL;
+  grpc_channel_destroy(cf->client);
+  cf->client = NULL;
+  gpr_free(cf->server_uri);
+}
+
+static void setup_server(const char *host, server_fixture *sf) {
+  int assigned_port;
+
+  sf->cq = grpc_completion_queue_create(NULL);
+  const char *colon_idx = strchr(host, ':');
+  if (colon_idx) {
+    const char *port_str = colon_idx + 1;
+    sf->port = atoi(port_str);
+    sf->servers_hostport = gpr_strdup(host);
+  } else {
+    sf->port = grpc_pick_unused_port_or_die();
+    gpr_join_host_port(&sf->servers_hostport, host, sf->port);
+  }
+
+  sf->server = grpc_server_create(NULL, NULL);
+  grpc_server_register_completion_queue(sf->server, sf->cq, NULL);
+  GPR_ASSERT((assigned_port = grpc_server_add_insecure_http2_port(
+                  sf->server, sf->servers_hostport)) > 0);
+  GPR_ASSERT(sf->port == assigned_port);
+  grpc_server_start(sf->server);
+}
+
+static void teardown_server(server_fixture *sf) {
+  if (!sf->server) return;
+
+  gpr_log(GPR_INFO, "Server[%s] shutting down", sf->servers_hostport);
+  grpc_server_shutdown_and_notify(sf->server, sf->cq, tag(1000));
+  GPR_ASSERT(grpc_completion_queue_pluck(
+                 sf->cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL)
+                 .type == GRPC_OP_COMPLETE);
+  grpc_server_destroy(sf->server);
+  gpr_thd_join(sf->tid);
+
+  sf->server = NULL;
+  grpc_completion_queue_shutdown(sf->cq);
+  drain_cq(sf->cq);
+  grpc_completion_queue_destroy(sf->cq);
+
+  gpr_log(GPR_INFO, "Server[%s] bye bye", sf->servers_hostport);
+  gpr_free(sf->servers_hostport);
+}
+
+static void fork_backend_server(void *arg) {
+  server_fixture *sf = static_cast<server_fixture *>(arg);
+  start_backend_server(sf);
+}
+
+static void fork_lb_server(void *arg) {
+  test_fixture *tf = static_cast<test_fixture *>(arg);
+  int ports[NUM_BACKENDS];
+  for (int i = 0; i < NUM_BACKENDS; i++) {
+    ports[i] = tf->lb_backends[i].port;
+  }
+  start_lb_server(&tf->lb_server, ports, NUM_BACKENDS,
+                  tf->lb_server_update_delay_ms);
+}
+
+static test_fixture setup_test_fixture(int lb_server_update_delay_ms) {
+  test_fixture tf;
+  memset(&tf, 0, sizeof(tf));
+  tf.lb_server_update_delay_ms = lb_server_update_delay_ms;
+
+  gpr_thd_options options = gpr_thd_options_default();
+  gpr_thd_options_set_joinable(&options);
+
+  for (int i = 0; i < NUM_BACKENDS; ++i) {
+    setup_server("127.0.0.1", &tf.lb_backends[i]);
+    gpr_thd_new(&tf.lb_backends[i].tid, fork_backend_server, &tf.lb_backends[i],
+                &options);
+  }
+
+  setup_server("127.0.0.1", &tf.lb_server);
+  gpr_thd_new(&tf.lb_server.tid, fork_lb_server, &tf.lb_server, &options);
+
+  char *server_uri;
+  gpr_asprintf(&server_uri, "test:%s?lb_policy=grpclb&lb_enabled=1",
+               tf.lb_server.servers_hostport);
+  setup_client(server_uri, &tf.client);
+  gpr_free(server_uri);
+
+  return tf;
+}
+
+static void teardown_test_fixture(test_fixture *tf) {
+  teardown_client(&tf->client);
+  for (int i = 0; i < NUM_BACKENDS; ++i) {
+    teardown_server(&tf->lb_backends[i]);
+  }
+  teardown_server(&tf->lb_server);
+}
+
+// The LB server will send two updates: batch 1 and batch 2. Each batch contains
+// two addresses, both of a valid and running backend server. Batch 1 is readily
+// available and provided as soon as the client establishes the streaming call.
+// Batch 2 is sent after a delay of \a lb_server_update_delay_ms milliseconds.
+static test_fixture test_update(int lb_server_update_delay_ms) {
+  gpr_log(GPR_INFO, "start %s(%d)", __func__, lb_server_update_delay_ms);
+  test_fixture tf = setup_test_fixture(lb_server_update_delay_ms);
+  perform_request(
+      &tf.client);  // "consumes" 1st backend server of 1st serverlist
+  perform_request(
+      &tf.client);  // "consumes" 2nd backend server of 1st serverlist
+
+  perform_request(
+      &tf.client);  // "consumes" 1st backend server of 2nd serverlist
+  perform_request(
+      &tf.client);  // "consumes" 2nd backend server of 2nd serverlist
+
+  teardown_test_fixture(&tf);
+  gpr_log(GPR_INFO, "end %s(%d)", __func__, lb_server_update_delay_ms);
+  return tf;
+}
+
+TEST(GrpclbTest, Updates) {
+  grpc::test_fixture tf_result;
+  // Clients take a bit over one second to complete a call (the last part of the
+  // call sleeps for 1 second while verifying the client's completion queue is
+  // empty). Therefore:
+  //
+  // If the LB server waits 800ms before sending an update, it will arrive
+  // before the first client request is done, skipping the second server from
+  // batch 1 altogether: the 2nd client request will go to the 1st server of
+  // batch 2 (ie, the third one out of the four total servers).
+  tf_result = grpc::test_update(800);
+  GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced == 1);
+  GPR_ASSERT(tf_result.lb_backends[1].num_calls_serviced == 0);
+  GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced == 2);
+  GPR_ASSERT(tf_result.lb_backends[3].num_calls_serviced == 1);
+
+  // If the LB server waits 1500ms, the update arrives after having picked the
+  // 2nd server from batch 1 but before the next pick for the first server of
+  // batch 2. All server are used.
+  tf_result = grpc::test_update(1500);
+  GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced == 1);
+  GPR_ASSERT(tf_result.lb_backends[1].num_calls_serviced == 1);
+  GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced == 1);
+  GPR_ASSERT(tf_result.lb_backends[3].num_calls_serviced == 1);
+
+  // If the LB server waits > 2000ms, the update arrives after the first two
+  // request are done and the third pick is performed, which returns, in RR
+  // fashion, the 1st server of the 1st update. Therefore, the second server of
+  // batch 1 is hit at least one, whereas the first server of batch 2 is never
+  // hit.
+  tf_result = grpc::test_update(2500);
+  GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced >= 1);
+  GPR_ASSERT(tf_result.lb_backends[1].num_calls_serviced > 0);
+  GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced > 0);
+  GPR_ASSERT(tf_result.lb_backends[3].num_calls_serviced == 0);
+}
+
+TEST(GrpclbTest, InvalidAddressInServerlist) {}
+
+}  // namespace
+}  // namespace grpc
+
+int main(int argc, char **argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  grpc_test_init(argc, argv);
+  grpc_fake_resolver_init();
+  grpc_init();
+  const auto result = RUN_ALL_TESTS();
+  grpc_shutdown();
+  return result;
+}
diff --git a/test/cpp/interop/client.cc b/test/cpp/interop/client.cc
index e8ae6ee..032b378 100644
--- a/test/cpp/interop/client.cc
+++ b/test/cpp/interop/client.cc
@@ -39,6 +39,7 @@
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
 
diff --git a/test/cpp/interop/interop_client.cc b/test/cpp/interop/interop_client.cc
index 8861bc1..6117878 100644
--- a/test/cpp/interop/interop_client.cc
+++ b/test/cpp/interop/interop_client.cc
@@ -40,6 +40,7 @@
 #include <grpc++/client_context.h>
 #include <grpc++/security/credentials.h>
 #include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/useful.h>
diff --git a/test/cpp/interop/interop_server.cc b/test/cpp/interop/interop_server.cc
index ebef000..c05eb5d 100644
--- a/test/cpp/interop/interop_server.cc
+++ b/test/cpp/interop/interop_server.cc
@@ -31,7 +31,6 @@
  *
  */
 
-#include <signal.h>
 #include <unistd.h>
 
 #include <fstream>
@@ -57,6 +56,7 @@
 
 DEFINE_bool(use_tls, false, "Whether to use tls.");
 DEFINE_int32(port, 0, "Server port.");
+DEFINE_int32(max_send_message_size, -1, "The maximum send message size.");
 
 using grpc::Server;
 using grpc::ServerBuilder;
@@ -78,8 +78,6 @@
 using grpc::testing::TestService;
 using grpc::Status;
 
-static bool got_sigint = false;
-
 const char kEchoInitialMetadataKey[] = "x-grpc-test-echo-initial";
 const char kEchoTrailingBinMetadataKey[] = "x-grpc-test-echo-trailing-bin";
 const char kEchoUserAgentKey[] = "x-grpc-test-echo-useragent";
@@ -311,7 +309,9 @@
   }
 };
 
-void RunServer() {
+void grpc::testing::interop::RunServer(
+    std::shared_ptr<ServerCredentials> creds) {
+  GPR_ASSERT(FLAGS_port != 0);
   std::ostringstream server_address;
   server_address << "0.0.0.0:" << FLAGS_port;
   TestServiceImpl service;
@@ -321,24 +321,13 @@
 
   ServerBuilder builder;
   builder.RegisterService(&service);
-  std::shared_ptr<ServerCredentials> creds =
-      grpc::testing::CreateInteropServerCredentials();
   builder.AddListeningPort(server_address.str(), creds);
+  if (FLAGS_max_send_message_size >= 0) {
+    builder.SetMaxSendMessageSize(FLAGS_max_send_message_size);
+  }
   std::unique_ptr<Server> server(builder.BuildAndStart());
   gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str());
-  while (!got_sigint) {
+  while (!g_got_sigint) {
     sleep(5);
   }
 }
-
-static void sigint_handler(int x) { got_sigint = true; }
-
-int main(int argc, char** argv) {
-  grpc::testing::InitTest(&argc, &argv, true);
-  signal(SIGINT, sigint_handler);
-
-  GPR_ASSERT(FLAGS_port != 0);
-  RunServer();
-
-  return 0;
-}
diff --git a/test/cpp/interop/interop_server_bootstrap.cc b/test/cpp/interop/interop_server_bootstrap.cc
new file mode 100644
index 0000000..424f7ca
--- /dev/null
+++ b/test/cpp/interop/interop_server_bootstrap.cc
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2016, 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 <signal.h>
+#include <unistd.h>
+
+#include "test/cpp/interop/server_helper.h"
+#include "test/cpp/util/test_config.h"
+
+bool grpc::testing::interop::g_got_sigint = false;
+
+static void sigint_handler(int x) {
+  grpc::testing::interop::g_got_sigint = true;
+}
+
+int main(int argc, char** argv) {
+  grpc::testing::InitTest(&argc, &argv, true);
+  signal(SIGINT, sigint_handler);
+
+  grpc::testing::interop::RunServer(
+      grpc::testing::CreateInteropServerCredentials());
+
+  return 0;
+}
diff --git a/test/cpp/interop/metrics_client.cc b/test/cpp/interop/metrics_client.cc
index 179de30..9d5212e 100644
--- a/test/cpp/interop/metrics_client.cc
+++ b/test/cpp/interop/metrics_client.cc
@@ -36,6 +36,7 @@
 
 #include <gflags/gflags.h>
 #include <grpc++/grpc++.h>
+#include <grpc/support/log.h>
 
 #include "src/proto/grpc/testing/metrics.grpc.pb.h"
 #include "src/proto/grpc/testing/metrics.pb.h"
diff --git a/test/cpp/interop/server_helper.h b/test/cpp/interop/server_helper.h
index a1da14a..fc4ea8b 100644
--- a/test/cpp/interop/server_helper.h
+++ b/test/cpp/interop/server_helper.h
@@ -60,6 +60,12 @@
   const ::grpc::ServerContext& context_;
 };
 
+namespace interop {
+
+extern bool g_got_sigint;
+void RunServer(std::shared_ptr<ServerCredentials> creds);
+
+}  // namespace interop
 }  // namespace testing
 }  // namespace grpc
 
diff --git a/test/cpp/interop/stress_interop_client.cc b/test/cpp/interop/stress_interop_client.cc
index 1d5fc80..0ea71e4 100644
--- a/test/cpp/interop/stress_interop_client.cc
+++ b/test/cpp/interop/stress_interop_client.cc
@@ -38,6 +38,7 @@
 #include <vector>
 
 #include <grpc++/create_channel.h>
+#include <grpc/support/log.h>
 
 #include "test/cpp/interop/interop_client.h"
 #include "test/cpp/util/metrics_server.h"
diff --git a/test/cpp/interop/stress_test.cc b/test/cpp/interop/stress_test.cc
index 7787931..5647cb5 100644
--- a/test/cpp/interop/stress_test.cc
+++ b/test/cpp/interop/stress_test.cc
@@ -41,6 +41,7 @@
 #include <grpc++/create_channel.h>
 #include <grpc++/grpc++.h>
 #include <grpc++/impl/thd.h>
+#include <grpc/support/log.h>
 #include <grpc/support/time.h>
 
 #include "src/proto/grpc/testing/metrics.grpc.pb.h"
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
index 4045e13..5fb87b2 100644
--- a/test/cpp/qps/client.h
+++ b/test/cpp/qps/client.h
@@ -129,13 +129,20 @@
 
 class Client {
  public:
-  Client() : timer_(new UsageTimer), interarrival_timer_() {}
+  Client()
+      : timer_(new UsageTimer),
+        interarrival_timer_(),
+        started_requests_(false) {
+    gpr_event_init(&start_requests_);
+  }
   virtual ~Client() {}
 
   ClientStats Mark(bool reset) {
     Histogram latencies;
     UsageTimer::Result timer_result;
 
+    MaybeStartRequests();
+
     // avoid std::vector for old compilers that expect a copy constructor
     if (reset) {
       Histogram* to_merge = new Histogram[threads_.size()];
@@ -169,6 +176,7 @@
   // Must call AwaitThreadsCompletion before destructor to avoid a race
   // between destructor and invocation of virtual ThreadFunc
   void AwaitThreadsCompletion() {
+    gpr_atm_rel_store(&thread_pool_done_, static_cast<gpr_atm>(true));
     DestroyMultithreading();
     std::unique_lock<std::mutex> g(thread_completion_mu_);
     while (threads_remaining_ != 0) {
@@ -178,15 +186,20 @@
 
  protected:
   bool closed_loop_;
+  gpr_atm thread_pool_done_;
 
   void StartThreads(size_t num_threads) {
+    gpr_atm_rel_store(&thread_pool_done_, static_cast<gpr_atm>(false));
     threads_remaining_ = num_threads;
     for (size_t i = 0; i < num_threads; i++) {
       threads_.emplace_back(new Thread(this, i));
     }
   }
 
-  void EndThreads() { threads_.clear(); }
+  void EndThreads() {
+    MaybeStartRequests();
+    threads_.clear();
+  }
 
   virtual void DestroyMultithreading() = 0;
   virtual bool ThreadFunc(HistogramEntry* histogram, size_t thread_idx) = 0;
@@ -241,18 +254,9 @@
   class Thread {
    public:
     Thread(Client* client, size_t idx)
-        : done_(false),
-          client_(client),
-          idx_(idx),
-          impl_(&Thread::ThreadFunc, this) {}
+        : client_(client), idx_(idx), impl_(&Thread::ThreadFunc, this) {}
 
-    ~Thread() {
-      {
-        std::lock_guard<std::mutex> g(mu_);
-        done_ = true;
-      }
-      impl_.join();
-    }
+    ~Thread() { impl_.join(); }
 
     void BeginSwap(Histogram* n) {
       std::lock_guard<std::mutex> g(mu_);
@@ -271,6 +275,13 @@
     Thread& operator=(const Thread&);
 
     void ThreadFunc() {
+      while (!gpr_event_wait(
+          &client_->start_requests_,
+          gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+                       gpr_time_from_seconds(1, GPR_TIMESPAN)))) {
+        gpr_log(GPR_INFO, "Waiting for benchmark to start");
+      }
+
       for (;;) {
         // run the loop body
         HistogramEntry entry;
@@ -282,9 +293,9 @@
         }
         if (!thread_still_ok) {
           gpr_log(GPR_ERROR, "Finishing client thread due to RPC error");
-          done_ = true;
         }
-        if (done_) {
+        if (!thread_still_ok ||
+            static_cast<bool>(gpr_atm_acq_load(&client_->thread_pool_done_))) {
           client_->CompleteThread();
           return;
         }
@@ -292,7 +303,6 @@
     }
 
     std::mutex mu_;
-    bool done_;
     Histogram histogram_;
     Client* client_;
     const size_t idx_;
@@ -309,6 +319,16 @@
   size_t threads_remaining_;
   std::condition_variable threads_complete_;
 
+  gpr_event start_requests_;
+  bool started_requests_;
+
+  void MaybeStartRequests() {
+    if (!started_requests_) {
+      started_requests_ = true;
+      gpr_event_set(&start_requests_, (void*)1);
+    }
+  }
+
   void CompleteThread() {
     std::lock_guard<std::mutex> g(thread_completion_mu_);
     threads_remaining_--;
@@ -366,7 +386,7 @@
       gpr_log(GPR_INFO, "Connecting to %s", target.c_str());
       GPR_ASSERT(channel_->WaitForConnected(
           gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
-                       gpr_time_from_seconds(30, GPR_TIMESPAN))));
+                       gpr_time_from_seconds(300, GPR_TIMESPAN))));
       stub_ = create_stub(channel_);
     }
     Channel* get_channel() { return channel_.get(); }
diff --git a/test/cpp/qps/client_sync.cc b/test/cpp/qps/client_sync.cc
index 25c7823..8062424 100644
--- a/test/cpp/qps/client_sync.cc
+++ b/test/cpp/qps/client_sync.cc
@@ -79,10 +79,29 @@
   virtual ~SynchronousClient(){};
 
  protected:
-  void WaitToIssue(int thread_idx) {
+  // WaitToIssue returns false if we realize that we need to break out
+  bool WaitToIssue(int thread_idx) {
     if (!closed_loop_) {
-      gpr_sleep_until(NextIssueTime(thread_idx));
+      const gpr_timespec next_issue_time = NextIssueTime(thread_idx);
+      // Avoid sleeping for too long continuously because we might
+      // need to terminate before then. This is an issue since
+      // exponential distribution can occasionally produce bad outliers
+      while (true) {
+        const gpr_timespec one_sec_delay =
+            gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                         gpr_time_from_seconds(1, GPR_TIMESPAN));
+        if (gpr_time_cmp(next_issue_time, one_sec_delay) <= 0) {
+          gpr_sleep_until(next_issue_time);
+          return true;
+        } else {
+          gpr_sleep_until(one_sec_delay);
+          if (gpr_atm_acq_load(&thread_pool_done_) != static_cast<gpr_atm>(0)) {
+            return false;
+          }
+        }
+      }
     }
+    return true;
   }
 
   size_t num_threads_;
@@ -101,7 +120,9 @@
   ~SynchronousUnaryClient() {}
 
   bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) GRPC_OVERRIDE {
-    WaitToIssue(thread_idx);
+    if (!WaitToIssue(thread_idx)) {
+      return true;
+    }
     auto* stub = channels_[thread_idx % channels_.size()].get_stub();
     double start = UsageTimer::Now();
     GPR_TIMER_SCOPE("SynchronousUnaryClient::ThreadFunc", 0);
@@ -144,7 +165,9 @@
   }
 
   bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) GRPC_OVERRIDE {
-    WaitToIssue(thread_idx);
+    if (!WaitToIssue(thread_idx)) {
+      return true;
+    }
     GPR_TIMER_SCOPE("SynchronousStreamingClient::ThreadFunc", 0);
     double start = UsageTimer::Now();
     if (stream_[thread_idx]->Write(request_) &&
diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc
index 2aeaea5..b4c18bc 100644
--- a/test/cpp/qps/driver.cc
+++ b/test/cpp/qps/driver.cc
@@ -186,6 +186,9 @@
     const ClientConfig& initial_client_config, size_t num_clients,
     const ServerConfig& initial_server_config, size_t num_servers,
     int warmup_seconds, int benchmark_seconds, int spawn_local_worker_count) {
+  // Log everything from the driver
+  gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG);
+
   // ClientContext allocations (all are destroyed at scope exit)
   list<ClientContext> contexts;
 
@@ -310,6 +313,7 @@
   // clients is array rather than std::vector to avoid gcc-4.4 issues
   // where class contained in std::vector must have a copy constructor
   auto* clients = new ClientData[num_clients];
+  size_t channels_allocated = 0;
   for (size_t i = 0; i < num_clients; i++) {
     const auto& worker = workers[i + num_servers];
     gpr_log(GPR_INFO, "Starting client on %s (worker #%" PRIuPTR ")",
@@ -345,6 +349,16 @@
       }
     }
 
+    // Reduce channel count so that total channels specified is held regardless
+    // of the number of clients available
+    size_t num_channels =
+        (client_config.client_channels() - channels_allocated) /
+        (num_clients - i);
+    channels_allocated += num_channels;
+    gpr_log(GPR_DEBUG, "Client %" PRIdPTR " gets %" PRIdPTR " channels", i,
+            num_channels);
+    per_client_config.set_client_channels(num_channels);
+
     ClientArgs args;
     *args.mutable_setup() = per_client_config;
     clients[i].stream =
@@ -352,12 +366,37 @@
     if (!clients[i].stream->Write(args)) {
       gpr_log(GPR_ERROR, "Could not write args to client %zu", i);
     }
+  }
+
+  for (size_t i = 0; i < num_clients; i++) {
     ClientStatus init_status;
     if (!clients[i].stream->Read(&init_status)) {
       gpr_log(GPR_ERROR, "Client %zu did not yield initial status", i);
     }
   }
 
+  // Send an initial mark: clients can use this to know that everything is ready
+  // to start
+  gpr_log(GPR_INFO, "Initiating");
+  ServerArgs server_mark;
+  server_mark.mutable_mark()->set_reset(true);
+  ClientArgs client_mark;
+  client_mark.mutable_mark()->set_reset(true);
+  ServerStatus server_status;
+  ClientStatus client_status;
+  for (size_t i = 0; i < num_clients; i++) {
+    auto client = &clients[i];
+    if (!client->stream->Write(client_mark)) {
+      gpr_log(GPR_ERROR, "Couldn't write mark to client %zu", i);
+    }
+  }
+  for (size_t i = 0; i < num_clients; i++) {
+    auto client = &clients[i];
+    if (!client->stream->Read(&client_status)) {
+      gpr_log(GPR_ERROR, "Couldn't get status from client %zu", i);
+    }
+  }
+
   // Let everything warmup
   gpr_log(GPR_INFO, "Warming up");
   gpr_timespec start = gpr_now(GPR_CLOCK_REALTIME);
@@ -366,10 +405,6 @@
 
   // Start a run
   gpr_log(GPR_INFO, "Starting");
-  ServerArgs server_mark;
-  server_mark.mutable_mark()->set_reset(true);
-  ClientArgs client_mark;
-  client_mark.mutable_mark()->set_reset(true);
   for (size_t i = 0; i < num_servers; i++) {
     auto server = &servers[i];
     if (!server->stream->Write(server_mark)) {
@@ -382,8 +417,6 @@
       gpr_log(GPR_ERROR, "Couldn't write mark to client %zu", i);
     }
   }
-  ServerStatus server_status;
-  ClientStatus client_status;
   for (size_t i = 0; i < num_servers; i++) {
     auto server = &servers[i];
     if (!server->stream->Read(&server_status)) {
diff --git a/test/cpp/qps/gen_build_yaml.py b/test/cpp/qps/gen_build_yaml.py
index 4ff4e44..369da2c 100755
--- a/test/cpp/qps/gen_build_yaml.py
+++ b/test/cpp/qps/gen_build_yaml.py
@@ -74,8 +74,8 @@
       'name': 'json_run_localhost',
       'shortname': 'json_run_localhost:%s' % scenario_json['name'],
       'args': ['--scenarios_json', _scenario_json_string(scenario_json)],
-      'ci_platforms': ['linux', 'mac', 'posix', 'windows'],
-      'platforms': ['linux', 'mac', 'posix', 'windows'],
+      'ci_platforms': ['linux'],
+      'platforms': ['linux'],
       'flaky': False,
       'language': 'c++',
       'boringssl': True,
@@ -85,5 +85,6 @@
       'timeout_seconds': 3*60
     }
     for scenario_json in scenario_config.CXXLanguage().scenarios()
+    if 'scalable' in scenario_json.get('CATEGORIES', [])
   ]
 })
diff --git a/test/cpp/qps/limit_cores.cc b/test/cpp/qps/limit_cores.cc
index 59ed369..b5c2225 100644
--- a/test/cpp/qps/limit_cores.cc
+++ b/test/cpp/qps/limit_cores.cc
@@ -68,9 +68,9 @@
       cores_set++;
     }
   }
-  GPR_ASSERT(sched_setaffinity(0, size, cpup) == 0);
+  bool affinity_set = (sched_setaffinity(0, size, cpup) == 0);
   CPU_FREE(cpup);
-  return cores_set;
+  return affinity_set ? cores_set : num_cores;
 }
 
 }  // namespace testing
diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc
index dea8746..082b4bc 100644
--- a/test/cpp/qps/server_async.cc
+++ b/test/cpp/qps/server_async.cc
@@ -108,14 +108,14 @@
           auto request_unary =
               std::bind(request_unary_function, &async_service_, _1, _2, _3,
                         srv_cqs_[j].get(), srv_cqs_[j].get(), _4);
-          contexts_.push_front(
+          contexts_.emplace_back(
               new ServerRpcContextUnaryImpl(request_unary, process_rpc_bound));
         }
         if (request_streaming_function) {
           auto request_streaming =
               std::bind(request_streaming_function, &async_service_, _1, _2,
                         srv_cqs_[j].get(), srv_cqs_[j].get(), _3);
-          contexts_.push_front(new ServerRpcContextStreamingImpl(
+          contexts_.emplace_back(new ServerRpcContextStreamingImpl(
               request_streaming, process_rpc_bound));
         }
       }
@@ -146,10 +146,6 @@
       while ((*cq)->Next(&got_tag, &ok))
         ;
     }
-    while (!contexts_.empty()) {
-      delete contexts_.front();
-      contexts_.pop_front();
-    }
   }
 
  private:
@@ -336,7 +332,7 @@
   std::unique_ptr<grpc::Server> server_;
   std::vector<std::unique_ptr<grpc::ServerCompletionQueue>> srv_cqs_;
   ServiceType async_service_;
-  std::forward_list<ServerRpcContext *> contexts_;
+  std::vector<std::unique_ptr<ServerRpcContext>> contexts_;
 
   struct PerThreadShutdownState {
     mutable std::mutex mutex;
diff --git a/test/cpp/util/byte_buffer_proto_helper.cc b/test/cpp/util/byte_buffer_proto_helper.cc
index 2512c9b..d625d6f 100644
--- a/test/cpp/util/byte_buffer_proto_helper.cc
+++ b/test/cpp/util/byte_buffer_proto_helper.cc
@@ -38,7 +38,7 @@
 
 bool ParseFromByteBuffer(ByteBuffer* buffer, grpc::protobuf::Message* message) {
   std::vector<Slice> slices;
-  buffer->Dump(&slices);
+  (void)buffer->Dump(&slices);
   grpc::string buf;
   buf.reserve(buffer->Length());
   for (auto s = slices.begin(); s != slices.end(); s++) {
diff --git a/test/cpp/util/byte_buffer_test.cc b/test/cpp/util/byte_buffer_test.cc
index 1167c79..2089a62 100644
--- a/test/cpp/util/byte_buffer_test.cc
+++ b/test/cpp/util/byte_buffer_test.cc
@@ -100,7 +100,7 @@
   slices.push_back(Slice(world, Slice::STEAL_REF));
   ByteBuffer buffer(&slices[0], 2);
   slices.clear();
-  buffer.Dump(&slices);
+  (void)buffer.Dump(&slices);
   EXPECT_TRUE(SliceEqual(slices[0], hello));
   EXPECT_TRUE(SliceEqual(slices[1], world));
 }
diff --git a/test/cpp/util/cli_call.cc b/test/cpp/util/cli_call.cc
index 98b9d93..1edffbe 100644
--- a/test/cpp/util/cli_call.cc
+++ b/test/cpp/util/cli_call.cc
@@ -94,7 +94,7 @@
 
   if (status.ok()) {
     std::vector<grpc::Slice> slices;
-    recv_buffer.Dump(&slices);
+    (void)recv_buffer.Dump(&slices);
 
     response->clear();
     for (size_t i = 0; i < slices.size(); i++) {
diff --git a/test/cpp/util/cli_credentials.cc b/test/cpp/util/cli_credentials.cc
new file mode 100644
index 0000000..8de9393
--- /dev/null
+++ b/test/cpp/util/cli_credentials.cc
@@ -0,0 +1,63 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/cpp/util/cli_credentials.h"
+
+#include <gflags/gflags.h>
+
+DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls.");
+DEFINE_bool(use_auth, false, "Whether to create default google credentials.");
+
+namespace grpc {
+namespace testing {
+
+std::shared_ptr<grpc::ChannelCredentials> CliCredentials::GetCredentials()
+    const {
+  if (!FLAGS_enable_ssl) {
+    return grpc::InsecureChannelCredentials();
+  } else {
+    if (FLAGS_use_auth) {
+      return grpc::GoogleDefaultCredentials();
+    } else {
+      return grpc::SslCredentials(grpc::SslCredentialsOptions());
+    }
+  }
+}
+
+const grpc::string CliCredentials::GetCredentialUsage() const {
+  return "    --enable_ssl             ; Set whether to use tls\n"
+         "    --use_auth               ; Set whether to create default google"
+         " credentials\n";
+}
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/util/cli_credentials.h b/test/cpp/util/cli_credentials.h
new file mode 100644
index 0000000..581b77a
--- /dev/null
+++ b/test/cpp/util/cli_credentials.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright 2016, 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_TEST_CPP_UTIL_CLI_CREDENTIALS_H
+#define GRPC_TEST_CPP_UTIL_CLI_CREDENTIALS_H
+
+#include <grpc++/security/credentials.h>
+#include <grpc++/support/config.h>
+
+namespace grpc {
+namespace testing {
+
+class CliCredentials {
+ public:
+  virtual ~CliCredentials() {}
+  virtual std::shared_ptr<grpc::ChannelCredentials> GetCredentials() const;
+  virtual const grpc::string GetCredentialUsage() const;
+};
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif  // GRPC_TEST_CPP_UTIL_CLI_CREDENTIALS_H
diff --git a/test/cpp/util/config_grpc_cli.h b/test/cpp/util/config_grpc_cli.h
new file mode 100644
index 0000000..ea8231a
--- /dev/null
+++ b/test/cpp/util/config_grpc_cli.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * Copyright 2016, 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_TEST_CPP_UTIL_CONFIG_GRPC_CLI_H
+#define GRPC_TEST_CPP_UTIL_CONFIG_GRPC_CLI_H
+
+#include <grpc++/impl/codegen/config_protobuf.h>
+
+#ifndef GRPC_CUSTOM_DYNAMICMESSAGEFACTORY
+#include <google/protobuf/dynamic_message.h>
+#define GRPC_CUSTOM_DYNAMICMESSAGEFACTORY \
+  ::google::protobuf::DynamicMessageFactory
+#endif
+
+#ifndef GRPC_CUSTOM_DESCRIPTORPOOLDATABASE
+#include <google/protobuf/descriptor.h>
+#define GRPC_CUSTOM_DESCRIPTORPOOLDATABASE \
+  ::google::protobuf::DescriptorPoolDatabase
+#define GRPC_CUSTOM_MERGEDDESCRIPTORDATABASE \
+  ::google::protobuf::MergedDescriptorDatabase
+#endif
+
+#ifndef GRPC_CUSTOM_TEXTFORMAT
+#include <google/protobuf/text_format.h>
+#define GRPC_CUSTOM_TEXTFORMAT ::google::protobuf::TextFormat
+#endif
+
+#ifndef GRPC_CUSTOM_DISKSOURCETREE
+#include <google/protobuf/compiler/importer.h>
+#define GRPC_CUSTOM_DISKSOURCETREE ::google::protobuf::compiler::DiskSourceTree
+#define GRPC_CUSTOM_IMPORTER ::google::protobuf::compiler::Importer
+#define GRPC_CUSTOM_MULTIFILEERRORCOLLECTOR \
+  ::google::protobuf::compiler::MultiFileErrorCollector
+#endif
+
+namespace grpc {
+namespace protobuf {
+
+typedef GRPC_CUSTOM_DYNAMICMESSAGEFACTORY DynamicMessageFactory;
+
+typedef GRPC_CUSTOM_DESCRIPTORPOOLDATABASE DescriptorPoolDatabase;
+typedef GRPC_CUSTOM_MERGEDDESCRIPTORDATABASE MergedDescriptorDatabase;
+
+typedef GRPC_CUSTOM_TEXTFORMAT TextFormat;
+
+namespace compiler {
+typedef GRPC_CUSTOM_DISKSOURCETREE DiskSourceTree;
+typedef GRPC_CUSTOM_IMPORTER Importer;
+typedef GRPC_CUSTOM_MULTIFILEERRORCOLLECTOR MultiFileErrorCollector;
+}  // namespace importer
+
+}  // namespace protobuf
+}  // namespace grpc
+
+#endif  // GRPC_TEST_CPP_UTIL_CONFIG_GRPC_CLI_H
diff --git a/test/cpp/util/grpc_cli.cc b/test/cpp/util/grpc_cli.cc
index 53529da..fe24860 100644
--- a/test/cpp/util/grpc_cli.cc
+++ b/test/cpp/util/grpc_cli.cc
@@ -33,12 +33,14 @@
 
 /*
   A command line tool to talk to a grpc server.
+  Run `grpc_cli help` command to see its usage information.
+
   Example of talking to grpc interop server:
   grpc_cli call localhost:50051 UnaryCall "response_size:10" \
       --protofiles=src/proto/grpc/testing/test.proto --enable_ssl=false
 
   Options:
-    1. --protofiles, use this flag to provide a proto file if the server does
+    1. --protofiles, use this flag to provide proto files if the server does
        does not have the reflection service.
     2. --proto_path, if your proto file is not under current working directory,
        use this flag to provide a search root. It should work similar to the
@@ -48,15 +50,17 @@
        --metadata="MyHeaderKey1:Value1:MyHeaderKey2:Value2"
     4. --enable_ssl, whether to use tls.
     5. --use_auth, if set to true, attach a GoogleDefaultCredentials to the call
-    6. --input_binary_file, a file containing the serialized request. The file
-       can be generated by calling something like:
+    6. --infile, input filename (defaults to stdin)
+    7. --outfile, output filename (defaults to stdout)
+    8. --binary_input, use the serialized request as input. The serialized
+       request can be generated by calling something like:
        protoc --proto_path=src/proto/grpc/testing/ \
          --encode=grpc.testing.SimpleRequest \
          src/proto/grpc/testing/messages.proto \
          < input.txt > input.bin
        If this is used and no proto file is provided in the argument list, the
        method string has to be exact in the form of /package.service/method.
-    7. --output_binary_file, a file to write binary format response into, it can
+    9. --binary_output, use binary format response as output, it can
        be later decoded using protoc:
        protoc --proto_path=src/proto/grpc/testing/ \
        --decode=grpc.testing.SimpleResponse \
@@ -64,182 +68,34 @@
        < output.bin > output.txt
 */
 
-#include <unistd.h>
 #include <fstream>
+#include <functional>
 #include <iostream>
-#include <sstream>
 
 #include <gflags/gflags.h>
-#include <grpc++/channel.h>
-#include <grpc++/create_channel.h>
-#include <grpc++/security/credentials.h>
-#include <grpc++/support/string_ref.h>
-#include <grpc/grpc.h>
-
-#include "test/cpp/util/cli_call.h"
-#include "test/cpp/util/proto_file_parser.h"
-#include "test/cpp/util/string_ref_helper.h"
+#include <grpc++/support/config.h>
+#include "test/cpp/util/cli_credentials.h"
+#include "test/cpp/util/grpc_tool.h"
 #include "test/cpp/util/test_config.h"
 
-DEFINE_bool(enable_ssl, true, "Whether to use ssl/tls.");
-DEFINE_bool(use_auth, false, "Whether to create default google credentials.");
-DEFINE_string(input_binary_file, "",
-              "Path to input file containing serialized request.");
-DEFINE_string(output_binary_file, "",
-              "Path to output file to write serialized response.");
-DEFINE_string(metadata, "",
-              "Metadata to send to server, in the form of key1:val1:key2:val2");
-DEFINE_string(proto_path, ".", "Path to look for the proto file.");
-// TODO(zyc): support a list of input proto files
-DEFINE_string(protofiles, "", "Name of the proto file.");
+DEFINE_string(outfile, "", "Output file (default is stdout)");
 
-void ParseMetadataFlag(
-    std::multimap<grpc::string, grpc::string>* client_metadata) {
-  if (FLAGS_metadata.empty()) {
-    return;
+static bool SimplePrint(const grpc::string& outfile,
+                        const grpc::string& output) {
+  if (outfile.empty()) {
+    std::cout << output;
+  } else {
+    std::ofstream output_file(outfile, std::ios::trunc | std::ios::binary);
+    output_file << output;
+    output_file.close();
   }
-  std::vector<grpc::string> fields;
-  const char* delim = ":";
-  size_t cur, next = -1;
-  do {
-    cur = next + 1;
-    next = FLAGS_metadata.find_first_of(delim, cur);
-    fields.push_back(FLAGS_metadata.substr(cur, next - cur));
-  } while (next != grpc::string::npos);
-  if (fields.size() % 2) {
-    std::cout << "Failed to parse metadata flag" << std::endl;
-    exit(1);
-  }
-  for (size_t i = 0; i < fields.size(); i += 2) {
-    client_metadata->insert(
-        std::pair<grpc::string, grpc::string>(fields[i], fields[i + 1]));
-  }
-}
-
-template <typename T>
-void PrintMetadata(const T& m, const grpc::string& message) {
-  if (m.empty()) {
-    return;
-  }
-  std::cout << message << std::endl;
-  grpc::string pair;
-  for (typename T::const_iterator iter = m.begin(); iter != m.end(); ++iter) {
-    pair.clear();
-    pair.append(iter->first.data(), iter->first.size());
-    pair.append(" : ");
-    pair.append(iter->second.data(), iter->second.size());
-    std::cout << pair << std::endl;
-  }
+  return true;
 }
 
 int main(int argc, char** argv) {
   grpc::testing::InitTest(&argc, &argv, true);
 
-  if (argc < 4 || grpc::string(argv[1]) != "call") {
-    std::cout << "Usage: grpc_cli call server_host:port method_name "
-              << "[proto file] [text format request] [<options>]" << std::endl;
-    return 1;
-  }
-
-  grpc::string request_text;
-  grpc::string server_address(argv[2]);
-  grpc::string method_name(argv[3]);
-  std::unique_ptr<grpc::testing::ProtoFileParser> parser;
-  grpc::string serialized_request_proto;
-
-  if (argc == 5) {
-    request_text = argv[4];
-  }
-
-  std::shared_ptr<grpc::ChannelCredentials> creds;
-  if (!FLAGS_enable_ssl) {
-    creds = grpc::InsecureChannelCredentials();
-  } else {
-    if (FLAGS_use_auth) {
-      creds = grpc::GoogleDefaultCredentials();
-    } else {
-      creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
-    }
-  }
-  std::shared_ptr<grpc::Channel> channel =
-      grpc::CreateChannel(server_address, creds);
-
-  if (request_text.empty() && FLAGS_input_binary_file.empty()) {
-    if (isatty(STDIN_FILENO)) {
-      std::cout << "reading request message from stdin..." << std::endl;
-    }
-    std::stringstream input_stream;
-    input_stream << std::cin.rdbuf();
-    request_text = input_stream.str();
-  }
-
-  if (!request_text.empty()) {
-    if (!FLAGS_protofiles.empty()) {
-      parser.reset(new grpc::testing::ProtoFileParser(
-          FLAGS_proto_path, FLAGS_protofiles, method_name));
-    } else {
-      parser.reset(new grpc::testing::ProtoFileParser(channel, method_name));
-    }
-    method_name = parser->GetFullMethodName();
-    if (parser->HasError()) {
-      return 1;
-    }
-
-    if (!FLAGS_input_binary_file.empty()) {
-      std::cout
-          << "warning: request given in argv, ignoring --input_binary_file"
-          << std::endl;
-    }
-  }
-
-  if (parser) {
-    serialized_request_proto =
-        parser->GetSerializedProto(request_text, true /* is_request */);
-    if (parser->HasError()) {
-      return 1;
-    }
-  } else if (!FLAGS_input_binary_file.empty()) {
-    std::ifstream input_file(FLAGS_input_binary_file,
-                             std::ios::in | std::ios::binary);
-    std::stringstream input_stream;
-    input_stream << input_file.rdbuf();
-    serialized_request_proto = input_stream.str();
-  }
-  std::cout << "connecting to " << server_address << std::endl;
-
-  grpc::string serialized_response_proto;
-  std::multimap<grpc::string, grpc::string> client_metadata;
-  std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata,
-      server_trailing_metadata;
-  ParseMetadataFlag(&client_metadata);
-  PrintMetadata(client_metadata, "Sending client initial metadata:");
-  grpc::Status s = grpc::testing::CliCall::Call(
-      channel, method_name, serialized_request_proto,
-      &serialized_response_proto, client_metadata, &server_initial_metadata,
-      &server_trailing_metadata);
-  PrintMetadata(server_initial_metadata,
-                "Received initial metadata from server:");
-  PrintMetadata(server_trailing_metadata,
-                "Received trailing metadata from server:");
-  if (s.ok()) {
-    std::cout << "Rpc succeeded with OK status" << std::endl;
-    if (parser) {
-      grpc::string response_text = parser->GetTextFormat(
-          serialized_response_proto, false /* is_request */);
-      if (parser->HasError()) {
-        return 1;
-      }
-      std::cout << "Response: \n " << response_text << std::endl;
-    }
-    if (!FLAGS_output_binary_file.empty()) {
-      std::ofstream output_file(FLAGS_output_binary_file,
-                                std::ios::trunc | std::ios::binary);
-      output_file << serialized_response_proto;
-    }
-  } else {
-    std::cout << "Rpc failed with status code " << s.error_code()
-              << ", error message: " << s.error_message() << std::endl;
-  }
-
-  return 0;
+  return grpc::testing::GrpcToolMainLib(
+      argc, (const char**)argv, grpc::testing::CliCredentials(),
+      std::bind(SimplePrint, FLAGS_outfile, std::placeholders::_1));
 }
diff --git a/test/cpp/util/grpc_tool.cc b/test/cpp/util/grpc_tool.cc
new file mode 100644
index 0000000..8fb325c
--- /dev/null
+++ b/test/cpp/util/grpc_tool.cc
@@ -0,0 +1,498 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/cpp/util/grpc_tool.h"
+
+#include <unistd.h>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include <gflags/gflags.h>
+#include <grpc++/channel.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/grpc++.h>
+#include <grpc++/security/credentials.h>
+#include <grpc++/support/string_ref.h>
+#include <grpc/grpc.h>
+
+#include "test/cpp/util/cli_call.h"
+#include "test/cpp/util/proto_file_parser.h"
+#include "test/cpp/util/proto_reflection_descriptor_database.h"
+#include "test/cpp/util/service_describer.h"
+#include "test/cpp/util/test_config.h"
+
+namespace grpc {
+namespace testing {
+
+DEFINE_bool(l, false, "Use a long listing format");
+DEFINE_bool(remotedb, true, "Use server types to parse and format messages");
+DEFINE_string(metadata, "",
+              "Metadata to send to server, in the form of key1:val1:key2:val2");
+DEFINE_string(proto_path, ".", "Path to look for the proto file.");
+DEFINE_string(protofiles, "", "Name of the proto file.");
+DEFINE_bool(binary_input, false, "Input in binary format");
+DEFINE_bool(binary_output, false, "Output in binary format");
+DEFINE_string(infile, "", "Input file (default is stdin)");
+
+namespace {
+
+class GrpcTool {
+ public:
+  explicit GrpcTool();
+  virtual ~GrpcTool() {}
+
+  bool Help(int argc, const char** argv, const CliCredentials& cred,
+            GrpcToolOutputCallback callback);
+  bool CallMethod(int argc, const char** argv, const CliCredentials& cred,
+                  GrpcToolOutputCallback callback);
+  bool ListServices(int argc, const char** argv, const CliCredentials& cred,
+                    GrpcToolOutputCallback callback);
+  bool PrintType(int argc, const char** argv, const CliCredentials& cred,
+                 GrpcToolOutputCallback callback);
+  // TODO(zyc): implement the following methods
+  // bool ListServices(int argc, const char** argv, GrpcToolOutputCallback
+  // callback);
+  // bool PrintTypeId(int argc, const char** argv, GrpcToolOutputCallback
+  // callback);
+  // bool ParseMessage(int argc, const char** argv, GrpcToolOutputCallback
+  // callback);
+  // bool ToText(int argc, const char** argv, GrpcToolOutputCallback callback);
+  // bool ToBinary(int argc, const char** argv, GrpcToolOutputCallback
+  // callback);
+
+  void SetPrintCommandMode(int exit_status) {
+    print_command_usage_ = true;
+    usage_exit_status_ = exit_status;
+  }
+
+ private:
+  void CommandUsage(const grpc::string& usage) const;
+  bool print_command_usage_;
+  int usage_exit_status_;
+  const grpc::string cred_usage_;
+};
+
+template <typename T>
+std::function<bool(GrpcTool*, int, const char**, const CliCredentials&,
+                   GrpcToolOutputCallback)>
+BindWith5Args(T&& func) {
+  return std::bind(std::forward<T>(func), std::placeholders::_1,
+                   std::placeholders::_2, std::placeholders::_3,
+                   std::placeholders::_4, std::placeholders::_5);
+}
+
+template <typename T>
+size_t ArraySize(T& a) {
+  return ((sizeof(a) / sizeof(*(a))) /
+          static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))));
+}
+
+void ParseMetadataFlag(
+    std::multimap<grpc::string, grpc::string>* client_metadata) {
+  if (FLAGS_metadata.empty()) {
+    return;
+  }
+  std::vector<grpc::string> fields;
+  const char* delim = ":";
+  size_t cur, next = -1;
+  do {
+    cur = next + 1;
+    next = FLAGS_metadata.find_first_of(delim, cur);
+    fields.push_back(FLAGS_metadata.substr(cur, next - cur));
+  } while (next != grpc::string::npos);
+  if (fields.size() % 2) {
+    fprintf(stderr, "Failed to parse metadata flag.\n");
+    exit(1);
+  }
+  for (size_t i = 0; i < fields.size(); i += 2) {
+    client_metadata->insert(
+        std::pair<grpc::string, grpc::string>(fields[i], fields[i + 1]));
+  }
+}
+
+template <typename T>
+void PrintMetadata(const T& m, const grpc::string& message) {
+  if (m.empty()) {
+    return;
+  }
+  fprintf(stderr, "%s\n", message.c_str());
+  grpc::string pair;
+  for (typename T::const_iterator iter = m.begin(); iter != m.end(); ++iter) {
+    pair.clear();
+    pair.append(iter->first.data(), iter->first.size());
+    pair.append(" : ");
+    pair.append(iter->second.data(), iter->second.size());
+    fprintf(stderr, "%s\n", pair.c_str());
+  }
+}
+
+struct Command {
+  const char* command;
+  std::function<bool(GrpcTool*, int, const char**, const CliCredentials&,
+                     GrpcToolOutputCallback)>
+      function;
+  int min_args;
+  int max_args;
+};
+
+const Command ops[] = {
+    {"help", BindWith5Args(&GrpcTool::Help), 0, INT_MAX},
+    {"ls", BindWith5Args(&GrpcTool::ListServices), 1, 3},
+    {"list", BindWith5Args(&GrpcTool::ListServices), 1, 3},
+    {"call", BindWith5Args(&GrpcTool::CallMethod), 2, 3},
+    {"type", BindWith5Args(&GrpcTool::PrintType), 2, 2},
+    // {"parse", BindWith5Args(&GrpcTool::ParseMessage), 2, 3},
+    // {"totext", BindWith5Args(&GrpcTool::ToText), 2, 3},
+    // {"tobinary", BindWith5Args(&GrpcTool::ToBinary), 2, 3},
+};
+
+void Usage(const grpc::string& msg) {
+  fprintf(
+      stderr,
+      "%s\n"
+      "  grpc_cli ls ...         ; List services\n"
+      "  grpc_cli call ...       ; Call method\n"
+      "  grpc_cli type ...       ; Print type\n"
+      // "  grpc_cli parse ...      ; Parse message\n"
+      // "  grpc_cli totext ...     ; Convert binary message to text\n"
+      // "  grpc_cli tobinary ...   ; Convert text message to binary\n"
+      "  grpc_cli help ...       ; Print this message, or per-command usage\n"
+      "\n",
+      msg.c_str());
+
+  exit(1);
+}
+
+const Command* FindCommand(const grpc::string& name) {
+  for (int i = 0; i < (int)ArraySize(ops); i++) {
+    if (name == ops[i].command) {
+      return &ops[i];
+    }
+  }
+  return NULL;
+}
+}  // namespace
+
+int GrpcToolMainLib(int argc, const char** argv, const CliCredentials& cred,
+                    GrpcToolOutputCallback callback) {
+  if (argc < 2) {
+    Usage("No command specified");
+  }
+
+  grpc::string command = argv[1];
+  argc -= 2;
+  argv += 2;
+
+  const Command* cmd = FindCommand(command);
+  if (cmd != NULL) {
+    GrpcTool grpc_tool;
+    if (argc < cmd->min_args || argc > cmd->max_args) {
+      // Force the command to print its usage message
+      fprintf(stderr, "\nWrong number of arguments for %s\n", command.c_str());
+      grpc_tool.SetPrintCommandMode(1);
+      return cmd->function(&grpc_tool, -1, NULL, cred, callback);
+    }
+    const bool ok = cmd->function(&grpc_tool, argc, argv, cred, callback);
+    return ok ? 0 : 1;
+  } else {
+    Usage("Invalid command '" + grpc::string(command.c_str()) + "'");
+  }
+  return 1;
+}
+
+GrpcTool::GrpcTool() : print_command_usage_(false), usage_exit_status_(0) {}
+
+void GrpcTool::CommandUsage(const grpc::string& usage) const {
+  if (print_command_usage_) {
+    fprintf(stderr, "\n%s%s\n", usage.c_str(),
+            (usage.empty() || usage[usage.size() - 1] != '\n') ? "\n" : "");
+    exit(usage_exit_status_);
+  }
+}
+
+bool GrpcTool::Help(int argc, const char** argv, const CliCredentials& cred,
+                    GrpcToolOutputCallback callback) {
+  CommandUsage(
+      "Print help\n"
+      "  grpc_cli help [subcommand]\n");
+
+  if (argc == 0) {
+    Usage("");
+  } else {
+    const Command* cmd = FindCommand(argv[0]);
+    if (cmd == NULL) {
+      Usage("Unknown command '" + grpc::string(argv[0]) + "'");
+    }
+    SetPrintCommandMode(0);
+    cmd->function(this, -1, NULL, cred, callback);
+  }
+  return true;
+}
+
+bool GrpcTool::ListServices(int argc, const char** argv,
+                            const CliCredentials& cred,
+                            GrpcToolOutputCallback callback) {
+  CommandUsage(
+      "List services\n"
+      "  grpc_cli ls <address> [<service>[/<method>]]\n"
+      "    <address>                ; host:port\n"
+      "    <service>                ; Exported service name\n"
+      "    <method>                 ; Method name\n"
+      "    --l                      ; Use a long listing format\n"
+      "    --outfile                ; Output filename (defaults to stdout)\n" +
+      cred.GetCredentialUsage());
+
+  grpc::string server_address(argv[0]);
+  std::shared_ptr<grpc::Channel> channel =
+      grpc::CreateChannel(server_address, cred.GetCredentials());
+  grpc::ProtoReflectionDescriptorDatabase desc_db(channel);
+  grpc::protobuf::DescriptorPool desc_pool(&desc_db);
+
+  std::vector<grpc::string> service_list;
+  if (!desc_db.GetServices(&service_list)) {
+    return false;
+  }
+
+  // If no service is specified, dump the list of services.
+  grpc::string output;
+  if (argc < 2) {
+    // List all services, if --l is passed, then include full description,
+    // otherwise include a summarized list only.
+    if (FLAGS_l) {
+      output = DescribeServiceList(service_list, desc_pool);
+    } else {
+      for (auto it = service_list.begin(); it != service_list.end(); it++) {
+        auto const& service = *it;
+        output.append(service);
+        output.append("\n");
+      }
+    }
+  } else {
+    grpc::string service_name;
+    grpc::string method_name;
+    std::stringstream ss(argv[1]);
+
+    // Remove leading slashes.
+    while (ss.peek() == '/') {
+      ss.get();
+    }
+
+    // Parse service and method names. Support the following patterns:
+    //   Service
+    //   Service Method
+    //   Service.Method
+    //   Service/Method
+    if (argc == 3) {
+      std::getline(ss, service_name, '/');
+      method_name = argv[2];
+    } else {
+      if (std::getline(ss, service_name, '/')) {
+        std::getline(ss, method_name);
+      }
+    }
+
+    const grpc::protobuf::ServiceDescriptor* service =
+        desc_pool.FindServiceByName(service_name);
+    if (service != nullptr) {
+      if (method_name.empty()) {
+        output = FLAGS_l ? DescribeService(service) : SummarizeService(service);
+      } else {
+        method_name.insert(0, 1, '.');
+        method_name.insert(0, service_name);
+        const grpc::protobuf::MethodDescriptor* method =
+            desc_pool.FindMethodByName(method_name);
+        if (method != nullptr) {
+          output = FLAGS_l ? DescribeMethod(method) : SummarizeMethod(method);
+        } else {
+          fprintf(stderr, "Method %s not found in service %s.\n",
+                  method_name.c_str(), service_name.c_str());
+          return false;
+        }
+      }
+    } else {
+      if (!method_name.empty()) {
+        fprintf(stderr, "Service %s not found.\n", service_name.c_str());
+        return false;
+      } else {
+        const grpc::protobuf::MethodDescriptor* method =
+            desc_pool.FindMethodByName(service_name);
+        if (method != nullptr) {
+          output = FLAGS_l ? DescribeMethod(method) : SummarizeMethod(method);
+        } else {
+          fprintf(stderr, "Service or method %s not found.\n",
+                  service_name.c_str());
+          return false;
+        }
+      }
+    }
+  }
+  return callback(output);
+}
+
+bool GrpcTool::PrintType(int argc, const char** argv,
+                         const CliCredentials& cred,
+                         GrpcToolOutputCallback callback) {
+  CommandUsage(
+      "Print type\n"
+      "  grpc_cli type <address> <type>\n"
+      "    <address>                ; host:port\n"
+      "    <type>                   ; Protocol buffer type name\n" +
+      cred.GetCredentialUsage());
+
+  grpc::string server_address(argv[0]);
+  std::shared_ptr<grpc::Channel> channel =
+      grpc::CreateChannel(server_address, cred.GetCredentials());
+  grpc::ProtoReflectionDescriptorDatabase desc_db(channel);
+  grpc::protobuf::DescriptorPool desc_pool(&desc_db);
+
+  grpc::string output;
+  const grpc::protobuf::Descriptor* descriptor =
+      desc_pool.FindMessageTypeByName(argv[1]);
+  if (descriptor != nullptr) {
+    output = descriptor->DebugString();
+  } else {
+    fprintf(stderr, "Type %s not found.\n", argv[1]);
+    return false;
+  }
+  return callback(output);
+}
+
+bool GrpcTool::CallMethod(int argc, const char** argv,
+                          const CliCredentials& cred,
+                          GrpcToolOutputCallback callback) {
+  CommandUsage(
+      "Call method\n"
+      "  grpc_cli call <address> <service>[.<method>] <request>\n"
+      "    <address>                ; host:port\n"
+      "    <service>                ; Exported service name\n"
+      "    <method>                 ; Method name\n"
+      "    <request>                ; Text protobuffer (overrides infile)\n"
+      "    --protofiles             ; Comma separated proto files used as a"
+      " fallback when parsing request/response\n"
+      "    --proto_path             ; The search path of proto files, valid"
+      " only when --protofiles is given\n"
+      "    --metadata               ; The metadata to be sent to the server\n"
+      "    --infile                 ; Input filename (defaults to stdin)\n"
+      "    --outfile                ; Output filename (defaults to stdout)\n"
+      "    --binary_input           ; Input in binary format\n"
+      "    --binary_output          ; Output in binary format\n" +
+      cred.GetCredentialUsage());
+
+  std::stringstream output_ss;
+  grpc::string request_text;
+  grpc::string server_address(argv[0]);
+  grpc::string method_name(argv[1]);
+  std::unique_ptr<grpc::testing::ProtoFileParser> parser;
+  grpc::string serialized_request_proto;
+
+  if (argc == 3) {
+    request_text = argv[2];
+    if (!FLAGS_infile.empty()) {
+      fprintf(stderr, "warning: request given in argv, ignoring --infile\n");
+    }
+  } else {
+    std::stringstream input_stream;
+    if (FLAGS_infile.empty()) {
+      if (isatty(STDIN_FILENO)) {
+        fprintf(stderr, "reading request message from stdin...\n");
+      }
+      input_stream << std::cin.rdbuf();
+    } else {
+      std::ifstream input_file(FLAGS_infile, std::ios::in | std::ios::binary);
+      input_stream << input_file.rdbuf();
+      input_file.close();
+    }
+    request_text = input_stream.str();
+  }
+
+  std::shared_ptr<grpc::Channel> channel =
+      grpc::CreateChannel(server_address, cred.GetCredentials());
+  if (!FLAGS_binary_input || !FLAGS_binary_output) {
+    parser.reset(
+        new grpc::testing::ProtoFileParser(FLAGS_remotedb ? channel : nullptr,
+                                           FLAGS_proto_path, FLAGS_protofiles));
+    if (parser->HasError()) {
+      return false;
+    }
+  }
+
+  if (FLAGS_binary_input) {
+    serialized_request_proto = request_text;
+  } else {
+    serialized_request_proto = parser->GetSerializedProtoFromMethod(
+        method_name, request_text, true /* is_request */);
+    if (parser->HasError()) {
+      return false;
+    }
+  }
+  fprintf(stderr, "connecting to %s\n", server_address.c_str());
+
+  grpc::string serialized_response_proto;
+  std::multimap<grpc::string, grpc::string> client_metadata;
+  std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata,
+      server_trailing_metadata;
+  ParseMetadataFlag(&client_metadata);
+  PrintMetadata(client_metadata, "Sending client initial metadata:");
+  grpc::Status status = grpc::testing::CliCall::Call(
+      channel, parser->GetFormatedMethodName(method_name),
+      serialized_request_proto, &serialized_response_proto, client_metadata,
+      &server_initial_metadata, &server_trailing_metadata);
+  PrintMetadata(server_initial_metadata,
+                "Received initial metadata from server:");
+  PrintMetadata(server_trailing_metadata,
+                "Received trailing metadata from server:");
+  if (status.ok()) {
+    fprintf(stderr, "Rpc succeeded with OK status\n");
+    if (FLAGS_binary_output) {
+      output_ss << serialized_response_proto;
+    } else {
+      grpc::string response_text = parser->GetTextFormatFromMethod(
+          method_name, serialized_response_proto, false /* is_request */);
+      if (parser->HasError()) {
+        return false;
+      }
+      output_ss << "Response: \n " << response_text << std::endl;
+    }
+  } else {
+    fprintf(stderr, "Rpc failed with status code %d, error message: %s\n",
+            status.error_code(), status.error_message().c_str());
+  }
+
+  return callback(output_ss.str());
+}
+
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/util/grpc_tool.h b/test/cpp/util/grpc_tool.h
new file mode 100644
index 0000000..df36802
--- /dev/null
+++ b/test/cpp/util/grpc_tool.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2016, 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_TEST_CPP_UTIL_GRPC_TOOL_H
+#define GRPC_TEST_CPP_UTIL_GRPC_TOOL_H
+
+#include <functional>
+
+#include <grpc++/support/config.h>
+
+#include "test/cpp/util/cli_credentials.h"
+
+namespace grpc {
+namespace testing {
+
+typedef std::function<bool(const grpc::string &)> GrpcToolOutputCallback;
+
+int GrpcToolMainLib(int argc, const char **argv, const CliCredentials &cred,
+                    GrpcToolOutputCallback callback);
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif  // GRPC_TEST_CPP_UTIL_GRPC_TOOL_H
diff --git a/test/cpp/util/grpc_tool_test.cc b/test/cpp/util/grpc_tool_test.cc
new file mode 100644
index 0000000..bad1579
--- /dev/null
+++ b/test/cpp/util/grpc_tool_test.cc
@@ -0,0 +1,381 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/cpp/util/grpc_tool.h"
+
+#include <sstream>
+
+#include <gflags/gflags.h>
+#include <grpc++/channel.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/ext/proto_server_reflection_plugin.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_context.h>
+#include <grpc/grpc.h>
+#include <gtest/gtest.h>
+
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "src/proto/grpc/testing/echo.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+#include "test/cpp/util/cli_credentials.h"
+#include "test/cpp/util/string_ref_helper.h"
+
+using grpc::testing::EchoRequest;
+using grpc::testing::EchoResponse;
+
+#define USAGE_REGEX "(  grpc_cli .+\n){2,10}"
+
+#define ECHO_TEST_SERVICE_SUMMARY \
+  "Echo\n"                        \
+  "RequestStream\n"               \
+  "ResponseStream\n"              \
+  "BidiStream\n"                  \
+  "Unimplemented\n"
+
+#define ECHO_TEST_SERVICE_DESCRIPTION                                         \
+  "filename: src/proto/grpc/testing/echo.proto\n"                             \
+  "package: grpc.testing;\n"                                                  \
+  "service EchoTestService {\n"                                               \
+  "  rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \
+  "{}\n"                                                                      \
+  "  rpc RequestStream(stream grpc.testing.EchoRequest) returns "             \
+  "(grpc.testing.EchoResponse) {}\n"                                          \
+  "  rpc ResponseStream(grpc.testing.EchoRequest) returns (stream "           \
+  "grpc.testing.EchoResponse) {}\n"                                           \
+  "  rpc BidiStream(stream grpc.testing.EchoRequest) returns (stream "        \
+  "grpc.testing.EchoResponse) {}\n"                                           \
+  "  rpc Unimplemented(grpc.testing.EchoRequest) returns "                    \
+  "(grpc.testing.EchoResponse) {}\n"                                          \
+  "}\n"                                                                       \
+  "\n"
+
+#define ECHO_METHOD_DESCRIPTION                                               \
+  "  rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \
+  "{}\n"
+
+namespace grpc {
+namespace testing {
+
+DECLARE_bool(l);
+
+namespace {
+
+class TestCliCredentials GRPC_FINAL : public grpc::testing::CliCredentials {
+ public:
+  std::shared_ptr<grpc::ChannelCredentials> GetCredentials() const
+      GRPC_OVERRIDE {
+    return InsecureChannelCredentials();
+  }
+  const grpc::string GetCredentialUsage() const GRPC_OVERRIDE { return ""; }
+};
+
+bool PrintStream(std::stringstream* ss, const grpc::string& output) {
+  (*ss) << output;
+  return true;
+}
+
+template <typename T>
+size_t ArraySize(T& a) {
+  return ((sizeof(a) / sizeof(*(a))) /
+          static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))));
+}
+
+}  // namespame
+
+class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
+ public:
+  Status Echo(ServerContext* context, const EchoRequest* request,
+              EchoResponse* response) GRPC_OVERRIDE {
+    if (!context->client_metadata().empty()) {
+      for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator
+               iter = context->client_metadata().begin();
+           iter != context->client_metadata().end(); ++iter) {
+        context->AddInitialMetadata(ToString(iter->first),
+                                    ToString(iter->second));
+      }
+    }
+    context->AddTrailingMetadata("trailing_key", "trailing_value");
+    response->set_message(request->message());
+    return Status::OK;
+  }
+};
+
+class GrpcToolTest : public ::testing::Test {
+ protected:
+  GrpcToolTest() {}
+
+  // SetUpServer cannot be used with EXPECT_EXIT. grpc_pick_unused_port_or_die()
+  // uses atexit() to free chosen ports, and it will spawn a new thread in
+  // resolve_address_posix.c:192 at exit time.
+  const grpc::string SetUpServer() {
+    std::ostringstream server_address;
+    int port = grpc_pick_unused_port_or_die();
+    server_address << "localhost:" << port;
+    // Setup server
+    ServerBuilder builder;
+    builder.AddListeningPort(server_address.str(), InsecureServerCredentials());
+    builder.RegisterService(&service_);
+    server_ = builder.BuildAndStart();
+    return server_address.str();
+  }
+
+  void ShutdownServer() { server_->Shutdown(); }
+
+  void ExitWhenError(int argc, const char** argv, const CliCredentials& cred,
+                     GrpcToolOutputCallback callback) {
+    int result = GrpcToolMainLib(argc, argv, cred, callback);
+    if (result) {
+      exit(result);
+    }
+  }
+
+  std::unique_ptr<Server> server_;
+  TestServiceImpl service_;
+  reflection::ProtoServerReflectionPlugin plugin_;
+};
+
+TEST_F(GrpcToolTest, NoCommand) {
+  // Test input "grpc_cli"
+  std::stringstream output_stream;
+  const char* argv[] = {"grpc_cli"};
+  // Exit with 1, print usage instruction in stderr
+  EXPECT_EXIT(
+      GrpcToolMainLib(
+          ArraySize(argv), argv, TestCliCredentials(),
+          std::bind(PrintStream, &output_stream, std::placeholders::_1)),
+      ::testing::ExitedWithCode(1), "No command specified\n" USAGE_REGEX);
+  // No output
+  EXPECT_TRUE(0 == output_stream.tellp());
+}
+
+TEST_F(GrpcToolTest, InvalidCommand) {
+  // Test input "grpc_cli"
+  std::stringstream output_stream;
+  const char* argv[] = {"grpc_cli", "abc"};
+  // Exit with 1, print usage instruction in stderr
+  EXPECT_EXIT(
+      GrpcToolMainLib(
+          ArraySize(argv), argv, TestCliCredentials(),
+          std::bind(PrintStream, &output_stream, std::placeholders::_1)),
+      ::testing::ExitedWithCode(1), "Invalid command 'abc'\n" USAGE_REGEX);
+  // No output
+  EXPECT_TRUE(0 == output_stream.tellp());
+}
+
+TEST_F(GrpcToolTest, HelpCommand) {
+  // Test input "grpc_cli help"
+  std::stringstream output_stream;
+  const char* argv[] = {"grpc_cli", "help"};
+  // Exit with 1, print usage instruction in stderr
+  EXPECT_EXIT(GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+                              std::bind(PrintStream, &output_stream,
+                                        std::placeholders::_1)),
+              ::testing::ExitedWithCode(1), USAGE_REGEX);
+  // No output
+  EXPECT_TRUE(0 == output_stream.tellp());
+}
+
+TEST_F(GrpcToolTest, ListCommand) {
+  // Test input "grpc_cli list localhost:<port>"
+  std::stringstream output_stream;
+
+  const grpc::string server_address = SetUpServer();
+  const char* argv[] = {"grpc_cli", "ls", server_address.c_str()};
+
+  FLAGS_l = false;
+  EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+                                   std::bind(PrintStream, &output_stream,
+                                             std::placeholders::_1)));
+  EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(),
+                          "grpc.testing.EchoTestService\n"
+                          "grpc.reflection.v1alpha.ServerReflection\n"));
+
+  ShutdownServer();
+}
+
+TEST_F(GrpcToolTest, ListOneService) {
+  // Test input "grpc_cli list localhost:<port> grpc.testing.EchoTestService"
+  std::stringstream output_stream;
+
+  const grpc::string server_address = SetUpServer();
+  const char* argv[] = {"grpc_cli", "ls", server_address.c_str(),
+                        "grpc.testing.EchoTestService"};
+  // without -l flag
+  FLAGS_l = false;
+  EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+                                   std::bind(PrintStream, &output_stream,
+                                             std::placeholders::_1)));
+  // Expected output: ECHO_TEST_SERVICE_SUMMARY
+  EXPECT_TRUE(0 ==
+              strcmp(output_stream.str().c_str(), ECHO_TEST_SERVICE_SUMMARY));
+
+  // with -l flag
+  output_stream.str(grpc::string());
+  output_stream.clear();
+  FLAGS_l = true;
+  EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+                                   std::bind(PrintStream, &output_stream,
+                                             std::placeholders::_1)));
+  // Expected output: ECHO_TEST_SERVICE_DESCRIPTION
+  EXPECT_TRUE(
+      0 == strcmp(output_stream.str().c_str(), ECHO_TEST_SERVICE_DESCRIPTION));
+
+  ShutdownServer();
+}
+
+TEST_F(GrpcToolTest, TypeCommand) {
+  // Test input "grpc_cli type localhost:<port> grpc.testing.EchoRequest"
+  std::stringstream output_stream;
+
+  const grpc::string server_address = SetUpServer();
+  const char* argv[] = {"grpc_cli", "type", server_address.c_str(),
+                        "grpc.testing.EchoRequest"};
+
+  EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+                                   std::bind(PrintStream, &output_stream,
+                                             std::placeholders::_1)));
+  const grpc::protobuf::Descriptor* desc =
+      grpc::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(
+          "grpc.testing.EchoRequest");
+  // Expected output: the DebugString of grpc.testing.EchoRequest
+  EXPECT_TRUE(0 ==
+              strcmp(output_stream.str().c_str(), desc->DebugString().c_str()));
+
+  ShutdownServer();
+}
+
+TEST_F(GrpcToolTest, ListOneMethod) {
+  // Test input "grpc_cli list localhost:<port> grpc.testing.EchoTestService"
+  std::stringstream output_stream;
+
+  const grpc::string server_address = SetUpServer();
+  const char* argv[] = {"grpc_cli", "ls", server_address.c_str(),
+                        "grpc.testing.EchoTestService.Echo"};
+  // without -l flag
+  FLAGS_l = false;
+  EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+                                   std::bind(PrintStream, &output_stream,
+                                             std::placeholders::_1)));
+  // Expected output: "Echo"
+  EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), "Echo\n"));
+
+  // with -l flag
+  output_stream.str(grpc::string());
+  output_stream.clear();
+  FLAGS_l = true;
+  EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+                                   std::bind(PrintStream, &output_stream,
+                                             std::placeholders::_1)));
+  // Expected output: ECHO_METHOD_DESCRIPTION
+  EXPECT_TRUE(0 ==
+              strcmp(output_stream.str().c_str(), ECHO_METHOD_DESCRIPTION));
+
+  ShutdownServer();
+}
+
+TEST_F(GrpcToolTest, TypeNotFound) {
+  // Test input "grpc_cli type localhost:<port> grpc.testing.DummyRequest"
+  std::stringstream output_stream;
+
+  const grpc::string server_address = SetUpServer();
+  const char* argv[] = {"grpc_cli", "type", server_address.c_str(),
+                        "grpc.testing.DummyRequest"};
+
+  EXPECT_DEATH(ExitWhenError(ArraySize(argv), argv, TestCliCredentials(),
+                             std::bind(PrintStream, &output_stream,
+                                       std::placeholders::_1)),
+               ".*Type grpc.testing.DummyRequest not found.*");
+
+  ShutdownServer();
+}
+
+TEST_F(GrpcToolTest, CallCommand) {
+  // Test input "grpc_cli call localhost:<port> Echo "message: 'Hello'"
+  std::stringstream output_stream;
+
+  const grpc::string server_address = SetUpServer();
+  const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo",
+                        "message: 'Hello'"};
+
+  EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+                                   std::bind(PrintStream, &output_stream,
+                                             std::placeholders::_1)));
+  // Expected output: "message: \"Hello\""
+  EXPECT_TRUE(NULL !=
+              strstr(output_stream.str().c_str(), "message: \"Hello\""));
+  ShutdownServer();
+}
+
+TEST_F(GrpcToolTest, TooFewArguments) {
+  // Test input "grpc_cli call Echo"
+  std::stringstream output_stream;
+  const char* argv[] = {"grpc_cli", "call", "Echo"};
+
+  // Exit with 1
+  EXPECT_EXIT(
+      GrpcToolMainLib(
+          ArraySize(argv), argv, TestCliCredentials(),
+          std::bind(PrintStream, &output_stream, std::placeholders::_1)),
+      ::testing::ExitedWithCode(1), ".*Wrong number of arguments for call.*");
+  // No output
+  EXPECT_TRUE(0 == output_stream.tellp());
+}
+
+TEST_F(GrpcToolTest, TooManyArguments) {
+  // Test input "grpc_cli call localhost:<port> Echo Echo "message: 'Hello'"
+  std::stringstream output_stream;
+  const char* argv[] = {"grpc_cli", "call", "localhost:10000",
+                        "Echo",     "Echo", "message: 'Hello'"};
+
+  // Exit with 1
+  EXPECT_EXIT(
+      GrpcToolMainLib(
+          ArraySize(argv), argv, TestCliCredentials(),
+          std::bind(PrintStream, &output_stream, std::placeholders::_1)),
+      ::testing::ExitedWithCode(1), ".*Wrong number of arguments for call.*");
+  // No output
+  EXPECT_TRUE(0 == output_stream.tellp());
+}
+
+}  // namespace testing
+}  // namespace grpc
+
+int main(int argc, char** argv) {
+  grpc_test_init(argc, argv);
+  ::testing::InitGoogleTest(&argc, argv);
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  return RUN_ALL_TESTS();
+}
diff --git a/test/cpp/util/metrics_server.cc b/test/cpp/util/metrics_server.cc
index 1c7cd63..9296d65 100644
--- a/test/cpp/util/metrics_server.cc
+++ b/test/cpp/util/metrics_server.cc
@@ -35,6 +35,7 @@
 
 #include <grpc++/server.h>
 #include <grpc++/server_builder.h>
+#include <grpc/support/log.h>
 
 #include "src/proto/grpc/testing/metrics.grpc.pb.h"
 #include "src/proto/grpc/testing/metrics.pb.h"
diff --git a/test/cpp/util/proto_file_parser.cc b/test/cpp/util/proto_file_parser.cc
index 5b0d925..01acb01 100644
--- a/test/cpp/util/proto_file_parser.cc
+++ b/test/cpp/util/proto_file_parser.cc
@@ -36,8 +36,8 @@
 #include <algorithm>
 #include <iostream>
 #include <sstream>
+#include <unordered_set>
 
-#include <google/protobuf/text_format.h>
 #include <grpc++/support/config.h>
 
 namespace grpc {
@@ -56,8 +56,7 @@
 }
 }  // namespace
 
-class ErrorPrinter
-    : public google::protobuf::compiler::MultiFileErrorCollector {
+class ErrorPrinter : public protobuf::compiler::MultiFileErrorCollector {
  public:
   explicit ErrorPrinter(ProtoFileParser* parser) : parser_(parser) {}
 
@@ -71,7 +70,7 @@
 
   void AddWarning(const grpc::string& filename, int line, int column,
                   const grpc::string& message) GRPC_OVERRIDE {
-    std::cout << "warning " << filename << " " << line << " " << column << " "
+    std::cerr << "warning " << filename << " " << line << " " << column << " "
               << message << std::endl;
   }
 
@@ -79,69 +78,82 @@
   ProtoFileParser* parser_;  // not owned
 };
 
-ProtoFileParser::ProtoFileParser(const grpc::string& proto_path,
-                                 const grpc::string& file_name,
-                                 const grpc::string& method)
-    : has_error_(false) {
-  source_tree_.MapPath("", proto_path);
-  error_printer_.reset(new ErrorPrinter(this));
-  importer_.reset(new google::protobuf::compiler::Importer(
-      &source_tree_, error_printer_.get()));
-  const auto* file_desc = importer_->Import(file_name);
-  if (!file_desc) {
-    LogError("");
-    return;
-  }
-  dynamic_factory_.reset(
-      new google::protobuf::DynamicMessageFactory(importer_->pool()));
-
-  std::vector<const google::protobuf::ServiceDescriptor*> service_desc_list;
-  for (int i = 0; i < file_desc->service_count(); i++) {
-    service_desc_list.push_back(file_desc->service(i));
-  }
-  InitProtoFileParser(method, service_desc_list);
-}
-
 ProtoFileParser::ProtoFileParser(std::shared_ptr<grpc::Channel> channel,
-                                 const grpc::string& method)
-    : has_error_(false),
-      desc_db_(new grpc::ProtoReflectionDescriptorDatabase(channel)),
-      desc_pool_(new google::protobuf::DescriptorPool(desc_db_.get())) {
+                                 const grpc::string& proto_path,
+                                 const grpc::string& protofiles)
+    : has_error_(false) {
   std::vector<std::string> service_list;
-  if (!desc_db_->GetServices(&service_list)) {
-    LogError(
-        "Failed to get services from the server, "
-        "it may not have the reflection service.\n"
-        "Please try to use the --protofiles option to provide a proto file.");
+  if (channel) {
+    reflection_db_.reset(new grpc::ProtoReflectionDescriptorDatabase(channel));
+    reflection_db_->GetServices(&service_list);
   }
-  if (has_error_) {
+
+  std::unordered_set<grpc::string> known_services;
+  if (!protofiles.empty()) {
+    source_tree_.MapPath("", proto_path);
+    error_printer_.reset(new ErrorPrinter(this));
+    importer_.reset(
+        new protobuf::compiler::Importer(&source_tree_, error_printer_.get()));
+
+    grpc::string file_name;
+    std::stringstream ss(protofiles);
+    while (std::getline(ss, file_name, ',')) {
+      const auto* file_desc = importer_->Import(file_name);
+      if (file_desc) {
+        for (int i = 0; i < file_desc->service_count(); i++) {
+          service_desc_list_.push_back(file_desc->service(i));
+          known_services.insert(file_desc->service(i)->full_name());
+        }
+      } else {
+        std::cerr << file_name << " not found" << std::endl;
+      }
+    }
+
+    file_db_.reset(new protobuf::DescriptorPoolDatabase(*importer_->pool()));
+  }
+
+  if (!reflection_db_ && !file_db_) {
+    LogError("No available proto database");
     return;
   }
-  dynamic_factory_.reset(
-      new google::protobuf::DynamicMessageFactory(desc_pool_.get()));
 
-  std::vector<const google::protobuf::ServiceDescriptor*> service_desc_list;
-  for (auto it = service_list.begin(); it != service_list.end(); it++) {
-    service_desc_list.push_back(desc_pool_->FindServiceByName(*it));
+  if (!reflection_db_) {
+    desc_db_ = std::move(file_db_);
+  } else if (!file_db_) {
+    desc_db_ = std::move(reflection_db_);
+  } else {
+    desc_db_.reset(new protobuf::MergedDescriptorDatabase(reflection_db_.get(),
+                                                          file_db_.get()));
   }
-  InitProtoFileParser(method, service_desc_list);
+
+  desc_pool_.reset(new protobuf::DescriptorPool(desc_db_.get()));
+  dynamic_factory_.reset(new protobuf::DynamicMessageFactory(desc_pool_.get()));
+
+  for (auto it = service_list.begin(); it != service_list.end(); it++) {
+    if (known_services.find(*it) == known_services.end()) {
+      if (const protobuf::ServiceDescriptor* service_desc =
+              desc_pool_->FindServiceByName(*it)) {
+        service_desc_list_.push_back(service_desc);
+        known_services.insert(*it);
+      }
+    }
+  }
 }
 
 ProtoFileParser::~ProtoFileParser() {}
 
-void ProtoFileParser::InitProtoFileParser(
-    const grpc::string& method,
-    const std::vector<const google::protobuf::ServiceDescriptor*>
-        service_desc_list) {
-  const google::protobuf::MethodDescriptor* method_descriptor = nullptr;
-  for (auto it = service_desc_list.begin(); it != service_desc_list.end();
+grpc::string ProtoFileParser::GetFullMethodName(const grpc::string& method) {
+  has_error_ = false;
+  const protobuf::MethodDescriptor* method_descriptor = nullptr;
+  for (auto it = service_desc_list_.begin(); it != service_desc_list_.end();
        it++) {
     const auto* service_desc = *it;
     for (int j = 0; j < service_desc->method_count(); j++) {
       const auto* method_desc = service_desc->method(j);
       if (MethodNameMatch(method_desc->full_name(), method)) {
         if (method_descriptor) {
-          std::ostringstream error_stream("Ambiguous method names: ");
+          std::ostringstream error_stream;
+          error_stream << "Ambiguous method names: ";
           error_stream << method_descriptor->full_name() << " ";
           error_stream << method_desc->full_name();
           LogError(error_stream.str());
@@ -154,28 +166,82 @@
     LogError("Method name not found");
   }
   if (has_error_) {
-    return;
+    return "";
   }
-  full_method_name_ = method_descriptor->full_name();
-  size_t last_dot = full_method_name_.find_last_of('.');
-  if (last_dot != grpc::string::npos) {
-    full_method_name_[last_dot] = '/';
-  }
-  full_method_name_.insert(full_method_name_.begin(), '/');
 
-  request_prototype_.reset(
-      dynamic_factory_->GetPrototype(method_descriptor->input_type())->New());
-  response_prototype_.reset(
-      dynamic_factory_->GetPrototype(method_descriptor->output_type())->New());
+  return method_descriptor->full_name();
 }
 
-grpc::string ProtoFileParser::GetSerializedProto(
-    const grpc::string& text_format_proto, bool is_request) {
+grpc::string ProtoFileParser::GetFormatedMethodName(
+    const grpc::string& method) {
+  has_error_ = false;
+  grpc::string formated_method_name = GetFullMethodName(method);
+  if (has_error_) {
+    return "";
+  }
+  size_t last_dot = formated_method_name.find_last_of('.');
+  if (last_dot != grpc::string::npos) {
+    formated_method_name[last_dot] = '/';
+  }
+  formated_method_name.insert(formated_method_name.begin(), '/');
+  return formated_method_name;
+}
+
+grpc::string ProtoFileParser::GetMessageTypeFromMethod(
+    const grpc::string& method, bool is_request) {
+  has_error_ = false;
+  grpc::string full_method_name = GetFullMethodName(method);
+  if (has_error_) {
+    return "";
+  }
+  const protobuf::MethodDescriptor* method_desc =
+      desc_pool_->FindMethodByName(full_method_name);
+  if (!method_desc) {
+    LogError("Method not found");
+    return "";
+  }
+
+  return is_request ? method_desc->input_type()->full_name()
+                    : method_desc->output_type()->full_name();
+}
+
+grpc::string ProtoFileParser::GetSerializedProtoFromMethod(
+    const grpc::string& method, const grpc::string& text_format_proto,
+    bool is_request) {
+  has_error_ = false;
+  grpc::string message_type_name = GetMessageTypeFromMethod(method, is_request);
+  if (has_error_) {
+    return "";
+  }
+  return GetSerializedProtoFromMessageType(message_type_name,
+                                           text_format_proto);
+}
+
+grpc::string ProtoFileParser::GetTextFormatFromMethod(
+    const grpc::string& method, const grpc::string& serialized_proto,
+    bool is_request) {
+  has_error_ = false;
+  grpc::string message_type_name = GetMessageTypeFromMethod(method, is_request);
+  if (has_error_) {
+    return "";
+  }
+  return GetTextFormatFromMessageType(message_type_name, serialized_proto);
+}
+
+grpc::string ProtoFileParser::GetSerializedProtoFromMessageType(
+    const grpc::string& message_type_name,
+    const grpc::string& text_format_proto) {
+  has_error_ = false;
   grpc::string serialized;
-  grpc::protobuf::Message* msg =
-      is_request ? request_prototype_.get() : response_prototype_.get();
-  bool ok =
-      google::protobuf::TextFormat::ParseFromString(text_format_proto, msg);
+  const protobuf::Descriptor* desc =
+      desc_pool_->FindMessageTypeByName(message_type_name);
+  if (!desc) {
+    LogError("Message type not found");
+    return "";
+  }
+  std::unique_ptr<grpc::protobuf::Message> msg(
+      dynamic_factory_->GetPrototype(desc)->New());
+  bool ok = protobuf::TextFormat::ParseFromString(text_format_proto, msg.get());
   if (!ok) {
     LogError("Failed to parse text format to proto.");
     return "";
@@ -188,16 +254,24 @@
   return serialized;
 }
 
-grpc::string ProtoFileParser::GetTextFormat(
-    const grpc::string& serialized_proto, bool is_request) {
-  grpc::protobuf::Message* msg =
-      is_request ? request_prototype_.get() : response_prototype_.get();
+grpc::string ProtoFileParser::GetTextFormatFromMessageType(
+    const grpc::string& message_type_name,
+    const grpc::string& serialized_proto) {
+  has_error_ = false;
+  const protobuf::Descriptor* desc =
+      desc_pool_->FindMessageTypeByName(message_type_name);
+  if (!desc) {
+    LogError("Message type not found");
+    return "";
+  }
+  std::unique_ptr<grpc::protobuf::Message> msg(
+      dynamic_factory_->GetPrototype(desc)->New());
   if (!msg->ParseFromString(serialized_proto)) {
     LogError("Failed to deserialize proto.");
     return "";
   }
   grpc::string text_format;
-  if (!google::protobuf::TextFormat::PrintToString(*msg, &text_format)) {
+  if (!protobuf::TextFormat::PrintToString(*msg.get(), &text_format)) {
     LogError("Failed to print proto message to text format");
     return "";
   }
@@ -206,7 +280,7 @@
 
 void ProtoFileParser::LogError(const grpc::string& error_msg) {
   if (!error_msg.empty()) {
-    std::cout << error_msg << std::endl;
+    std::cerr << error_msg << std::endl;
   }
   has_error_ = true;
 }
diff --git a/test/cpp/util/proto_file_parser.h b/test/cpp/util/proto_file_parser.h
index b442d77..eda3991 100644
--- a/test/cpp/util/proto_file_parser.h
+++ b/test/cpp/util/proto_file_parser.h
@@ -36,11 +36,9 @@
 
 #include <memory>
 
-#include <google/protobuf/compiler/importer.h>
-#include <google/protobuf/dynamic_message.h>
 #include <grpc++/channel.h>
 
-#include "src/compiler/config.h"
+#include "test/cpp/util/config_grpc_cli.h"
 #include "test/cpp/util/proto_reflection_descriptor_database.h"
 
 namespace grpc {
@@ -50,44 +48,63 @@
 // Find method and associated request/response types.
 class ProtoFileParser {
  public:
-  // The given proto file_name will be searched in a source tree rooted from
-  // proto_path. The method could be a partial string such as Service.Method or
-  // even just Method. It will log an error if there is ambiguity.
-  ProtoFileParser(const grpc::string& proto_path, const grpc::string& file_name,
-                  const grpc::string& method);
-
+  // The parser will search proto files using the server reflection service
+  // provided on the given channel. The given protofiles in a source tree rooted
+  // from proto_path will also be searched.
   ProtoFileParser(std::shared_ptr<grpc::Channel> channel,
-                  const grpc::string& method);
+                  const grpc::string& proto_path,
+                  const grpc::string& protofiles);
+
   ~ProtoFileParser();
 
-  grpc::string GetFullMethodName() const { return full_method_name_; }
+  // The input method name in the following four functions could be a partial
+  // string such as Service.Method or even just Method. It will log an error if
+  // there is ambiguity.
+  // Full method name is in the form of Service.Method, it's good to be used in
+  // descriptor database queries.
+  grpc::string GetFullMethodName(const grpc::string& method);
 
-  grpc::string GetSerializedProto(const grpc::string& text_format_proto,
-                                  bool is_request);
+  // Formated method name is in the form of /Service/Method, it's good to be
+  // used as the argument of Stub::Call()
+  grpc::string GetFormatedMethodName(const grpc::string& method);
 
-  grpc::string GetTextFormat(const grpc::string& serialized_proto,
-                             bool is_request);
+  grpc::string GetSerializedProtoFromMethod(
+      const grpc::string& method, const grpc::string& text_format_proto,
+      bool is_request);
+
+  grpc::string GetTextFormatFromMethod(const grpc::string& method,
+                                       const grpc::string& serialized_proto,
+                                       bool is_request);
+
+  grpc::string GetSerializedProtoFromMessageType(
+      const grpc::string& message_type_name,
+      const grpc::string& text_format_proto);
+
+  grpc::string GetTextFormatFromMessageType(
+      const grpc::string& message_type_name,
+      const grpc::string& serialized_proto);
 
   bool HasError() const { return has_error_; }
 
   void LogError(const grpc::string& error_msg);
 
  private:
-  void InitProtoFileParser(
-      const grpc::string& method,
-      const std::vector<const google::protobuf::ServiceDescriptor*> services);
+  grpc::string GetMessageTypeFromMethod(const grpc::string& method,
+                                        bool is_request);
 
   bool has_error_;
   grpc::string request_text_;
-  grpc::string full_method_name_;
-  google::protobuf::compiler::DiskSourceTree source_tree_;
+  protobuf::compiler::DiskSourceTree source_tree_;
   std::unique_ptr<ErrorPrinter> error_printer_;
-  std::unique_ptr<google::protobuf::compiler::Importer> importer_;
-  std::unique_ptr<grpc::ProtoReflectionDescriptorDatabase> desc_db_;
-  std::unique_ptr<google::protobuf::DescriptorPool> desc_pool_;
-  std::unique_ptr<google::protobuf::DynamicMessageFactory> dynamic_factory_;
+  std::unique_ptr<protobuf::compiler::Importer> importer_;
+  std::unique_ptr<grpc::ProtoReflectionDescriptorDatabase> reflection_db_;
+  std::unique_ptr<protobuf::DescriptorPoolDatabase> file_db_;
+  std::unique_ptr<protobuf::DescriptorDatabase> desc_db_;
+  std::unique_ptr<protobuf::DescriptorPool> desc_pool_;
+  std::unique_ptr<protobuf::DynamicMessageFactory> dynamic_factory_;
   std::unique_ptr<grpc::protobuf::Message> request_prototype_;
   std::unique_ptr<grpc::protobuf::Message> response_prototype_;
+  std::vector<const protobuf::ServiceDescriptor*> service_desc_list_;
 };
 
 }  // namespace testing
diff --git a/test/cpp/util/proto_reflection_descriptor_database.cc b/test/cpp/util/proto_reflection_descriptor_database.cc
index 8fd466f..f0d14c6 100644
--- a/test/cpp/util/proto_reflection_descriptor_database.cc
+++ b/test/cpp/util/proto_reflection_descriptor_database.cc
@@ -58,7 +58,7 @@
     stream_->WritesDone();
     Status status = stream_->Finish();
     if (!status.ok()) {
-      gpr_log(GPR_ERROR,
+      gpr_log(GPR_INFO,
               "ServerReflectionInfo rpc failed. Error code: %d, details: %s",
               (int)status.error_code(), status.error_message().c_str());
     }
diff --git a/test/cpp/util/proto_reflection_descriptor_database.h b/test/cpp/util/proto_reflection_descriptor_database.h
index eb7cf49..0e69696 100644
--- a/test/cpp/util/proto_reflection_descriptor_database.h
+++ b/test/cpp/util/proto_reflection_descriptor_database.h
@@ -45,6 +45,8 @@
 #include <grpc++/ext/reflection.grpc.pb.h>
 #endif  // GRPC_NO_GENERATED_CODE
 #include <grpc++/grpc++.h>
+#include <grpc++/impl/codegen/config_protobuf.h>
+
 namespace grpc {
 
 // ProtoReflectionDescriptorDatabase takes a stub of ServerReflection and
diff --git a/test/cpp/util/service_describer.cc b/test/cpp/util/service_describer.cc
new file mode 100644
index 0000000..4fe4a74
--- /dev/null
+++ b/test/cpp/util/service_describer.cc
@@ -0,0 +1,108 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "test/cpp/util/service_describer.h"
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace grpc {
+namespace testing {
+
+grpc::string DescribeServiceList(std::vector<grpc::string> service_list,
+                                 grpc::protobuf::DescriptorPool& desc_pool) {
+  std::stringstream result;
+  for (auto it = service_list.begin(); it != service_list.end(); it++) {
+    auto const& service = *it;
+    const grpc::protobuf::ServiceDescriptor* service_desc =
+        desc_pool.FindServiceByName(service);
+    if (service_desc != nullptr) {
+      result << DescribeService(service_desc);
+    }
+  }
+  return result.str();
+}
+
+grpc::string DescribeService(const grpc::protobuf::ServiceDescriptor* service) {
+  grpc::string result;
+  if (service->options().deprecated()) {
+    result.append("DEPRECATED\n");
+  }
+  result.append("filename: " + service->file()->name() + "\n");
+
+  grpc::string package = service->full_name();
+  size_t pos = package.rfind("." + service->name());
+  if (pos != grpc::string::npos) {
+    package.erase(pos);
+    result.append("package: " + package + ";\n");
+  }
+  result.append("service " + service->name() + " {\n");
+  for (int i = 0; i < service->method_count(); ++i) {
+    result.append(DescribeMethod(service->method(i)));
+  }
+  result.append("}\n\n");
+  return result;
+}
+
+grpc::string DescribeMethod(const grpc::protobuf::MethodDescriptor* method) {
+  std::stringstream result;
+  result << "  rpc " << method->name()
+         << (method->client_streaming() ? "(stream " : "(")
+         << method->input_type()->full_name() << ") returns "
+         << (method->server_streaming() ? "(stream " : "(")
+         << method->output_type()->full_name() << ") {}\n";
+  if (method->options().deprecated()) {
+    result << " DEPRECATED";
+  }
+  return result.str();
+}
+
+grpc::string SummarizeService(
+    const grpc::protobuf::ServiceDescriptor* service) {
+  grpc::string result;
+  for (int i = 0; i < service->method_count(); ++i) {
+    result.append(SummarizeMethod(service->method(i)));
+  }
+  return result;
+}
+
+grpc::string SummarizeMethod(const grpc::protobuf::MethodDescriptor* method) {
+  grpc::string result = method->name();
+  result.append("\n");
+  return result;
+}
+
+}  // namespace testing
+}  // namespace grpc
diff --git a/test/cpp/util/service_describer.h b/test/cpp/util/service_describer.h
new file mode 100644
index 0000000..02c58e8
--- /dev/null
+++ b/test/cpp/util/service_describer.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * Copyright 2016, 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_TEST_CPP_UTIL_SERVICE_DESCRIBER_H
+#define GRPC_TEST_CPP_UTIL_SERVICE_DESCRIBER_H
+
+#include <grpc++/support/config.h>
+#include "test/cpp/util/config_grpc_cli.h"
+
+namespace grpc {
+namespace testing {
+
+grpc::string DescribeServiceList(std::vector<grpc::string> service_list,
+                                 grpc::protobuf::DescriptorPool& desc_pool);
+
+grpc::string DescribeService(const grpc::protobuf::ServiceDescriptor* service);
+
+grpc::string DescribeMethod(const grpc::protobuf::MethodDescriptor* method);
+
+grpc::string SummarizeService(const grpc::protobuf::ServiceDescriptor* service);
+
+grpc::string SummarizeMethod(const grpc::protobuf::MethodDescriptor* method);
+
+}  // namespase testing
+}  // namespace grpc
+
+#endif  // GRPC_TEST_CPP_UTIL_SERVICE_DESCRIBER_H
diff --git a/test/cpp/util/test_config.cc b/test/cpp/util/test_config_cc.cc
similarity index 100%
rename from test/cpp/util/test_config.cc
rename to test/cpp/util/test_config_cc.cc
diff --git a/test/cpp/util/test_credentials_provider.cc b/test/cpp/util/test_credentials_provider.cc
index 6e68f59..ca15f29 100644
--- a/test/cpp/util/test_credentials_provider.cc
+++ b/test/cpp/util/test_credentials_provider.cc
@@ -37,6 +37,7 @@
 #include <unordered_map>
 
 #include <grpc++/impl/sync.h>
+#include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 
 #include "test/core/end2end/data/ssl_test_data.h"
diff --git a/test/distrib/csharp/DistribTest/.gitignore b/test/distrib/csharp/DistribTest/.gitignore
index 1746e32..07f3d57 100644
--- a/test/distrib/csharp/DistribTest/.gitignore
+++ b/test/distrib/csharp/DistribTest/.gitignore
@@ -1,2 +1,3 @@
 bin
 obj
+*.lock.json
diff --git a/test/distrib/csharp/DistribTest/App.config b/test/distrib/csharp/DistribTest/App.config
deleted file mode 100644
index 30d3e09..0000000
--- a/test/distrib/csharp/DistribTest/App.config
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
-    <startup> 
-        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
-    </startup>
-  <runtime>
-    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
-      <dependentAssembly>
-        <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
-        <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" />
-      </dependentAssembly>
-    </assemblyBinding>
-  </runtime>
-</configuration>
\ No newline at end of file
diff --git a/test/distrib/csharp/DistribTest/DistribTest.csproj b/test/distrib/csharp/DistribTest/DistribTest.csproj
index 1acb34d..0bff9ff 100644
--- a/test/distrib/csharp/DistribTest/DistribTest.csproj
+++ b/test/distrib/csharp/DistribTest/DistribTest.csproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -41,6 +41,8 @@
     <ErrorReport>prompt</ErrorReport>
     <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
     <Prefer32Bit>true</Prefer32Bit>
+    <WarningLevel>4</WarningLevel>
+    <Optimize>false</Optimize>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
     <OutputPath>bin\x64\Release\</OutputPath>
@@ -51,65 +53,49 @@
     <ErrorReport>prompt</ErrorReport>
     <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
     <Prefer32Bit>true</Prefer32Bit>
+    <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="BouncyCastle.Crypto">
-      <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Auth">
-      <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Auth.PlatformServices">
-      <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
-    </Reference>
-    <Reference Include="Google.Apis.Core">
-      <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
-    </Reference>
     <Reference Include="Grpc.Auth">
       <HintPath>..\packages\Grpc.Auth.__GRPC_NUGET_VERSION__\lib\net45\Grpc.Auth.dll</HintPath>
     </Reference>
     <Reference Include="Grpc.Core">
       <HintPath>..\packages\Grpc.Core.__GRPC_NUGET_VERSION__\lib\net45\Grpc.Core.dll</HintPath>
     </Reference>
-    <Reference Include="Microsoft.Threading.Tasks">
-      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
-    </Reference>
-    <Reference Include="Microsoft.Threading.Tasks.Extensions">
-      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
-    </Reference>
-    <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop">
-      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
-    </Reference>
-    <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="System.Interactive.Async">
-      <HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath>
+      <HintPath>..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll</HintPath>
     </Reference>
     <Reference Include="System.Net" />
     <Reference Include="System.Net.Http" />
-    <Reference Include="System.Net.Http.Extensions">
-      <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
-    </Reference>
-    <Reference Include="System.Net.Http.Primitives">
-      <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
-    </Reference>
     <Reference Include="System.Net.Http.WebRequest" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Data" />
     <Reference Include="System.Xml" />
+    <Reference Include="BouncyCastle.Crypto">
+      <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json">
+      <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Core">
+      <HintPath>..\packages\Google.Apis.Core.1.15.0\lib\net45\Google.Apis.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth">
+      <HintPath>..\packages\Google.Apis.Auth.1.15.0\lib\net45\Google.Apis.Auth.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth.PlatformServices">
+      <HintPath>..\packages\Google.Apis.Auth.1.15.0\lib\net45\Google.Apis.Auth.PlatformServices.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Program.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>
-    <None Include="App.config" />
     <None Include="packages.config" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
@@ -119,9 +105,7 @@
       <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
     </PropertyGroup>
     <Error Condition="!Exists('..\packages\Grpc.Core.__GRPC_NUGET_VERSION__\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.__GRPC_NUGET_VERSION__\build\net45\Grpc.Core.targets'))" />
-    <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
   </Target>
-  <Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
   <Target Name="BeforeBuild">
diff --git a/test/distrib/csharp/DistribTest/DistribTest.project.json b/test/distrib/csharp/DistribTest/DistribTest.project.json
new file mode 100644
index 0000000..422545e
--- /dev/null
+++ b/test/distrib/csharp/DistribTest/DistribTest.project.json
@@ -0,0 +1,11 @@
+// This file exists only to prevent VS2015 from mistakenly picking up
+// project.json file when building .csproj project.
+// See https://github.com/Microsoft/msbuild/issues/394
+{
+  "frameworks": {
+    "net45": { }
+  },
+  "runtimes": {
+    "win": { }
+  }
+}
diff --git a/test/distrib/csharp/DistribTest/Properties/AssemblyInfo.cs b/test/distrib/csharp/DistribTest/Properties/AssemblyInfo.cs
index bc25b26..eacc41e 100644
--- a/test/distrib/csharp/DistribTest/Properties/AssemblyInfo.cs
+++ b/test/distrib/csharp/DistribTest/Properties/AssemblyInfo.cs
@@ -1,4 +1,37 @@
-using System.Reflection;
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
diff --git a/test/distrib/csharp/DistribTest/packages.config b/test/distrib/csharp/DistribTest/packages.config
index 6963019..84be8f7 100644
--- a/test/distrib/csharp/DistribTest/packages.config
+++ b/test/distrib/csharp/DistribTest/packages.config
@@ -1,15 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
-  <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" />
-  <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" />
+  <package id="Google.Apis.Auth" version="1.15.0" targetFramework="net45" />
+  <package id="Google.Apis.Core" version="1.15.0" targetFramework="net45" />
   <package id="Grpc" version="__GRPC_NUGET_VERSION__" targetFramework="net45" />
   <package id="Grpc.Auth" version="__GRPC_NUGET_VERSION__" targetFramework="net45" />
   <package id="Grpc.Core" version="__GRPC_NUGET_VERSION__" targetFramework="net45" />
-  <package id="Ix-Async" version="1.2.3" targetFramework="net45" />
-  <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
-  <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
-  <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
-  <package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.0.0" targetFramework="net45" />
   <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
-</packages>
+</packages>
\ No newline at end of file
diff --git a/test/distrib/csharp/DistribTest/project.json b/test/distrib/csharp/DistribTest/project.json
new file mode 100644
index 0000000..09266e5
--- /dev/null
+++ b/test/distrib/csharp/DistribTest/project.json
@@ -0,0 +1,22 @@
+{
+  "buildOptions": {
+    "emitEntryPoint": true
+  },
+  "dependencies": {
+    "Grpc.Auth": "__GRPC_NUGET_VERSION__",
+    "Grpc.Core": "__GRPC_NUGET_VERSION__",
+    // Necessary for native deps to get copied correctly.
+    "Microsoft.NETCore.Platforms": "1.0.1"
+  },
+  "frameworks": {
+    "net45": { },
+    "netcoreapp1.0": {
+      "dependencies": {
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        }
+      }
+    }
+  }
+}
diff --git a/test/distrib/csharp/run_distrib_test.bat b/test/distrib/csharp/run_distrib_test.bat
index 67bfc58..6cf3811 100644
--- a/test/distrib/csharp/run_distrib_test.bat
+++ b/test/distrib/csharp/run_distrib_test.bat
@@ -31,12 +31,22 @@
 cd /d %~dp0
 
 @rem extract input artifacts
-powershell -Command "Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('../../../input_artifacts/csharp_nugets.zip', 'TestNugetFeed');"
+powershell -Command "Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('../../../input_artifacts/csharp_nugets_dotnetcli.zip', 'TestNugetFeed');"
 
 update_version.sh auto
 
 set NUGET=C:\nuget\nuget.exe
-%NUGET% restore || goto :error
+
+@rem TODO(jtattermusch): Get rid of this hack. See #8034
+@rem We can't do just "nuget restore" because restoring a .sln solution doesn't work
+@rem with nuget 3.X. On the other hand, we need nuget 2.12+ to be able to restore
+@rem some of the packages (e.g. System.Interactive.Async), but nuget 2.12
+@rem hasn't been officially released.
+@rem Please note that "Restore nuget packages" in VS2013 and VS2015 GUI works as usual.
+
+cd DistribTest || goto :error
+%NUGET% restore -PackagesDirectory ../packages || goto :error
+cd ..
 
 @call build_vs2015.bat DistribTest.sln %MSBUILD_EXTRA_ARGS% || goto :error
 
diff --git a/test/distrib/csharp/run_distrib_test.sh b/test/distrib/csharp/run_distrib_test.sh
index 64e98d8..0a77c1a 100755
--- a/test/distrib/csharp/run_distrib_test.sh
+++ b/test/distrib/csharp/run_distrib_test.sh
@@ -32,7 +32,7 @@
 
 cd $(dirname $0)
 
-unzip -o "$EXTERNAL_GIT_ROOT/input_artifacts/csharp_nugets.zip" -d TestNugetFeed
+unzip -o "$EXTERNAL_GIT_ROOT/input_artifacts/csharp_nugets_dotnetcli.zip" -d TestNugetFeed
 
 ./update_version.sh auto
 
diff --git a/test/distrib/csharp/run_distrib_test_dotnetcli.sh b/test/distrib/csharp/run_distrib_test_dotnetcli.sh
new file mode 100755
index 0000000..493c504
--- /dev/null
+++ b/test/distrib/csharp/run_distrib_test_dotnetcli.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -ex
+
+cd $(dirname $0)
+
+unzip -o "$EXTERNAL_GIT_ROOT/input_artifacts/csharp_nugets_dotnetcli.zip" -d TestNugetFeed
+
+./update_version.sh auto
+
+cd DistribTest
+
+# TODO(jtattermusch): make sure we don't pollute the global nuget cache with
+# the nugets being tested.
+dotnet restore
+
+dotnet build
+dotnet publish
+
+# .NET 4.5 target after dotnet build
+mono bin/Debug/net45/*-x64/DistribTest.exe
+
+# .NET 4.5 target after dotnet publish
+mono bin/Debug/net45/*-x64/publish/DistribTest.exe
+
+# .NET Core target after dotnet build
+dotnet exec bin/Debug/netcoreapp1.0/DistribTest.dll
+
+# .NET Core target after dotnet publish
+dotnet exec bin/Debug/netcoreapp1.0/publish/DistribTest.dll
diff --git a/test/distrib/csharp/update_version.sh b/test/distrib/csharp/update_version.sh
index 189d0ad..2961d0c 100755
--- a/test/distrib/csharp/update_version.sh
+++ b/test/distrib/csharp/update_version.sh
@@ -35,10 +35,10 @@
 CSHARP_VERSION="$1"
 if [ "$CSHARP_VERSION" == "auto" ]
 then
-  # autodetect C# version
-  CSHARP_VERSION=$(ls TestNugetFeed | grep '^Grpc\.[0-9].*\.nupkg$' | sed s/^Grpc\.// | sed s/\.nupkg$//)
+  # autodetect C# version from the name of Grpc.Core.0.0.0-x.nupkg file
+  CSHARP_VERSION=$(ls TestNugetFeed | grep -m 1 '^Grpc\.Core\.[0-9].*\.nupkg$' | sed s/^Grpc\.Core\.// | sed s/\.nupkg$// | sed s/\.symbols$//)
   echo "Autodetected nuget ${CSHARP_VERSION}"
 fi
 
 # Replaces version placeholder with value provided as first argument.
-sed -ibak "s/__GRPC_NUGET_VERSION__/${CSHARP_VERSION}/g" DistribTest/packages.config DistribTest/DistribTest.csproj
+sed -ibak "s/__GRPC_NUGET_VERSION__/${CSHARP_VERSION}/g" DistribTest/packages.config DistribTest/DistribTest.csproj DistribTest/project.json
diff --git a/test/distrib/php/run_distrib_test.sh b/test/distrib/php/run_distrib_test.sh
index 5c86283..9ce3e05 100755
--- a/test/distrib/php/run_distrib_test.sh
+++ b/test/distrib/php/run_distrib_test.sh
@@ -32,8 +32,9 @@
 
 cd $(dirname $0)
 
-cp -r $EXTERNAL_GIT_ROOT/input_artifacts/grpc-php.tgz .
+cp -r $EXTERNAL_GIT_ROOT/input_artifacts/grpc-*.tgz .
 
-pecl install grpc-php.tgz
+find . -regextype sed -regex ".*/grpc-[0-9].*.tgz" | cut -b3- | \
+    xargs pecl install
 
 php -d extension=grpc.so -d max_execution_time=300 distribtest.php
diff --git a/test/distrib/python/distribtest.py b/test/distrib/python/distribtest.py
index dc20688..0125ee6 100644
--- a/test/distrib/python/distribtest.py
+++ b/test/distrib/python/distribtest.py
@@ -27,10 +27,10 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from grpc.beta import implementations
+import grpc
 
 # This code doesn't do much but makes sure the native extension is loaded
 # which is what we are testing here.
-channel = implementations.insecure_channel('localhost', 1000)
+channel = grpc.insecure_channel('localhost:1000')
 del channel
 print 'Success!'
diff --git a/third_party/nanopb b/third_party/nanopb
deleted file mode 160000
index f8ac463..0000000
--- a/third_party/nanopb
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit f8ac463766281625ad710900479130c7fcb4d63b
diff --git a/third_party/nanopb/.gitignore b/third_party/nanopb/.gitignore
new file mode 100644
index 0000000..3bb09db
--- /dev/null
+++ b/third_party/nanopb/.gitignore
@@ -0,0 +1,28 @@
+*.gcda
+*.gcno
+*.gcov
+*.o
+*.pb.c
+*.pb.h
+*.pb
+*.pyc
+*_pb2.py
+*~
+*.tar.gz
+.sconsign.dblite
+config.log
+.sconf_temp
+tests/build
+julkaisu.txt
+dist
+docs/*.html
+docs/generator_flow.png
+examples/simple/simple
+examples/network_server/client
+examples/network_server/server
+examples/using_double_on_avr/decode_double
+examples/using_double_on_avr/encode_double
+examples/using_double_on_avr/test_conversions
+examples/using_union_messages/decode
+examples/using_union_messages/encode
+generator/nanopb_pb2.pyc
diff --git a/third_party/nanopb/.travis.yml b/third_party/nanopb/.travis.yml
new file mode 100644
index 0000000..2e77e7c
--- /dev/null
+++ b/third_party/nanopb/.travis.yml
@@ -0,0 +1,54 @@
+# Travis CI has no ability to handle 3 langauges (c, c++, python)
+# and it overrides $CC/$CXX if language is set to c/c++ (only one, not both).
+#
+# Set language to python since at least the result of that is something useful.
+language: python
+
+python:
+    - "2.7"
+    - "3.4"
+
+# Manage the C/C++ compiler manually
+env:
+    - CC=gcc     CXX=g++
+    - CC=gcc-4.8 CXX=g++-4.8
+    - CC=gcc-4.9 CXX=g++-4.9
+    - CC=gcc-5   CXX=g++-5
+    - CC=clang   CXX=clang++
+
+addons:
+    apt:
+        sources:
+            - ubuntu-toolchain-r-test
+        packages:
+            - gcc-4.8
+            - g++-4.8
+            - gcc-4.9
+            - g++-4.9
+            - gcc-5
+            - g++-5
+
+
+before_install:
+    - export PATH=$HOME/.local/bin:$HOME/protobuf/bin:$PATH
+    - export MAKEFLAGS=-j$(grep processor /proc/cpuinfo | wc -l)
+    - $CC --version
+    - $CXX --version
+    - python --version
+    - lsb_release -a
+
+# Seems to be issues with concurrent builds
+#cache:
+#    directories:
+#        - $HOME/protobuf
+
+install:
+    - curl -L https://github.com/google/protobuf/releases/download/v3.0.0-beta-1/protobuf-python-3.0.0-alpha-4.tar.gz | tar xzf -
+      && pushd protobuf-3.0.0-alpha-4
+      && ./configure --prefix=$HOME/protobuf && make && make install
+      && pushd python && python setup.py build && python setup.py install && popd
+      && popd
+
+script:
+    - pushd generator/proto && make && popd
+    - pushd tests && python2 $(which scons) CC=$CC CXX=$CXX && popd
diff --git a/third_party/nanopb/AUTHORS b/third_party/nanopb/AUTHORS
new file mode 100644
index 0000000..a967d3e
--- /dev/null
+++ b/third_party/nanopb/AUTHORS
@@ -0,0 +1,24 @@
+Petteri Aimonen <jpa@npb.mail.kapsi.fi>
+Michael Poole <mdpoole@troilus.org>
+Daniel Kan <extremeblue99@gmail.com>
+Stan Hu <stanhu@aclimalabs.com>
+dch <david.hotham@blueyonder.co.uk>
+Steffen Siering <steffen siering gmail com>
+Jens Steinhauser <jens.steinhauser@gmail.com>
+Pavel Ilin <ilin.pa@gmail.com>
+Kent Ryhorchuk <kryhorchuk@xeralux.com>
+Martin Donath <scifish@gmail.com>
+Oliver Lee <oliverzlee@gmail.com>
+Michael Haberler <git@mah.priv.at>
+Nicolas Colomer <ncolomer@viadeoteam.com>
+Ivan Kravets <me@ikravets.com>
+Kyle Manna <kyle@kylemanna.com>
+Benjamin Kamath <ben.kamath@synapse.com>
+Andrew Ruder <andrew.ruder@elecsyscorp.com>
+Kenshi Kawaguchi <kenshi@recurse.ca>
+isotes <isotes@gmail.com>
+Maxim Khitrov <max@mxcrypt.com>
+Yaniv Mordekhay <yanivmo@users.noreply.github.com>
+Ming Zhao <mzhao@luminatewireless.com>
+Google, Inc.
+Tom Roeder <tmroeder@google.com>
diff --git a/third_party/nanopb/BUILD b/third_party/nanopb/BUILD
new file mode 100644
index 0000000..5709884
--- /dev/null
+++ b/third_party/nanopb/BUILD
@@ -0,0 +1,18 @@
+licenses(["notice"])
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+  name = "nanopb",
+  visibility = ["//visibility:public"],
+  hdrs = [
+    "pb.h",
+    "pb_common.h",
+    "pb_decode.h",
+    "pb_encode.h",
+  ],
+  srcs = [
+    "pb_common.c",
+    "pb_decode.c",
+    "pb_encode.c",
+  ],
+)
diff --git a/third_party/nanopb/CHANGELOG.txt b/third_party/nanopb/CHANGELOG.txt
new file mode 100644
index 0000000..8437688
--- /dev/null
+++ b/third_party/nanopb/CHANGELOG.txt
@@ -0,0 +1,241 @@
+nanopb-0.3.6 (2016-06-19)
+ Protect against corrupted _count fields in pb_release (#205)
+ Fix error in STATIC_ASSERT with multiple files (#203)
+ Add -D option to specify output directory (#193)
+ Generate MIN/MAX/ARRAYSIZE defines for enums (#194)
+ Generate comments about uncalculable message sizes (#195)
+ Documentation updates (#196, #201)
+ Improvements to test cases.
+
+nanopb-0.3.5 (2016-02-13)
+ NOTE: If you are using pb_syshdr.h, you will need to add uint_least8_t
+ definition. See docs/migration.rst for details.
+
+ Fix generator crash with Enum inside Oneof (#188)
+ Fix some generator regressions related to .options file path (#172)
+ Add support for platforms without uint8_t (#191)
+ Allow const parameter to pb_istream_from_buffer (#152)
+ Ignore null pointers in pb_release() (#183)
+ Add support for anonymous unions (#184)
+ Add Python3 support to the generator (#169)
+ Add code generator insertion points to generated files (#178)
+ Improvements to CMake script (#181)
+ Improvements to test cases.
+
+nanopb-0.3.4 (2015-09-26)
+ Fix handling of unsigned 8- and 16-bit enums (issue 164)
+ Fix generator on systems where python = python3. (issue 155)
+ Fix compiler warning on GCC 5.x (issue 171)
+ Make the generator better handle imported .protos (issue 165)
+ Add packed_enum option to generator.
+ Add syntax= line to .proto files (issue 167)
+ Add PlatformIO registry manifest file. (pr 156)
+
+nanopb-0.3.3 (2015-04-10)
+ Fix missing files in Linux binary package (issue 146)
+ Fix generator bug when oneof is first field in a message. (issue 142)
+ Fix generator error when long_names:false is combined with Oneofs. (issue 147)
+ Fix oneof submessage initialization bug. (issue 149)
+ Fix problem with plugin options on Python 2.7.2 and older. (issue 153)
+ Fix crash when callback is inside oneof field. (issue 148)
+ Switch to .tar.gz format for Mac OS X packages. (issue 154)
+ Always define enum long names so that cross-file references work. (issue 118)
+ Add msgid generator option. (issue 151)
+ Improve comment support in .options files. (issue 145)
+ Updates for the CMake rule file, add cmake example.
+ Better error messages for syntax errors in .options file
+
+nanopb-0.3.2 (2015-01-24)
+ Fix memory leaks with PB_ENABLE_MALLOC with some submessage hierarchies (issue 138)
+ Implement support for oneofs (C unions). (issues 131, 141)
+ Add int_size option for generator (issue 139)
+ Add compilation option to disable struct packing. (issue 136)
+ Change PB_RETURN_ERROR() macro to avoid compiler warnings (issue 140)
+ Fix build problems with protoc 3.0.0
+ Add support for POINTER type in extensions
+ Initialize also extension fields to defaults in pb_decode().
+ Detect too large varint values when decoding.
+
+nanopb-0.3.1 (2014-09-11)
+ Fix security issue due to size_t overflows. (issue 132)
+ Fix memory leak with duplicated fields and PB_ENABLE_MALLOC
+ Fix crash if pb_release() is called twice.
+ Fix cyclic message support (issue 130)
+ Fix error in generated initializers for repeated pointer fields.
+ Improve tests (issues 113, 126)
+
+nanopb-0.3.0 (2014-08-26)
+ NOTE: See docs/migration.html or online at
+ http://koti.kapsi.fi/~jpa/nanopb/docs/migration.html
+ for changes in this version. Most importantly, you need to add
+ pb_common.c to the list of files to compile.
+
+ Separated field iterator logic to pb_common.c (issue 128)
+ Change the _count fields to use pb_size_t datatype (issue 82)
+ Added PB_ prefix to macro names (issue 106)
+ Added #if version guard to generated files (issue 129)
+ Added migration document
+
+nanopb-0.2.9 (2014-08-09)
+ NOTE: If you are using the -e option with the generator, you have
+ to prepend . to the argument to get the same behaviour as before.
+
+ Do not automatically add a dot with generator -e option. (issue 122)
+ Fix problem with .options file and extension fields. (issue 125)
+ Don't use SIZE_MAX macro, as it is not in C89. (issue 120)
+ Generate #defines for initializing message structures. (issue 79)
+ Add skip_message option to generator. (issue 121)
+ Add PB_PACKED_STRUCT support for Keil MDK-ARM toolchain (issue 119)
+ Give better messages about the .options file path. (issue 124)
+ Improved tests
+
+nanopb-0.2.8 (2014-05-20)
+ Fix security issue with PB_ENABLE_MALLOC. (issue 117)
+ Add option to not add timestamps to .pb.h and .pb.c preambles. (issue 115)
+ Documentation updates
+ Improved tests
+
+nanopb-0.2.7 (2014-04-07)
+ Fix bug with default values for extension fields (issue 111)
+ Fix some MISRA-C warnings (issue 91)
+ Implemented optional malloc() support (issue 80)
+ Changed pointer-type bytes field datatype
+ Add a "found" field to pb_extension_t (issue 112)
+ Add convenience function pb_get_encoded_size() (issue 16)
+
+nanopb-0.2.6 (2014-02-15)
+ Fix generator error with bytes callback fields (issue 99)
+ Fix warnings about large integer constants (issue 102)
+ Add comments to where STATIC_ASSERT is used (issue 96)
+ Add warning about unknown field names on .options (issue 105)
+ Move descriptor.proto to google/protobuf subdirectory (issue 104)
+ Improved tests
+
+nanopb-0.2.5 (2014-01-01)
+ Fix a bug with encoding negative values in int32 fields (issue 97)
+ Create binary packages of the generator + dependencies (issue 47)
+ Add support for pointer-type fields to the encoder (part of issue 80)
+ Fixed path in FindNanopb.cmake (issue 94)
+ Improved tests
+
+nanopb-0.2.4 (2013-11-07)
+ Remove the deprecated NANOPB_INTERNALS functions from public API.
+ Document the security model.
+ Check array and bytes max sizes when encoding (issue 90)
+ Add #defines for maximum encoded message size (issue 89)
+ Add #define tags for extension fields (issue 93)
+ Fix MISRA C violations (issue 91)
+ Clean up pb_field_t definition with typedefs.
+
+nanopb-0.2.3 (2013-09-18)
+ Improve compatibility by removing ternary operator from initializations (issue 88)
+ Fix build error on Visual C++ (issue 84, patch by Markus Schwarzenberg)
+ Don't stop on unsupported extension fields (issue 83)
+ Add an example pb_syshdr.h file for non-C99 compilers
+ Reorganize tests and examples into subfolders (issue 63)
+ Switch from Makefiles to scons for building the tests
+ Make the tests buildable on Windows
+
+nanopb-0.2.2 (2013-08-18)
+ Add support for extension fields (issue 17)
+ Fix unknown fields in empty message (issue 78)
+ Include the field tags in the generated .pb.h file.
+ Add pb_decode_delimited and pb_encode_delimited wrapper functions (issue 74)
+ Add a section in top of pb.h for changing compilation settings (issue 76)
+ Documentation improvements (issues 12, 77 and others)
+ Improved tests
+
+nanopb-0.2.1 (2013-04-14)
+ NOTE: The default callback function signature has changed.
+       If you don't want to update your code, define PB_OLD_CALLBACK_STYLE.
+ 
+ Change the callback function to use void** (issue 69)
+ Add support for defining the nanopb options in a separate file (issue 12)
+ Add support for packed structs in IAR and MSVC (in addition to GCC) (issue 66)
+ Implement error message support for the encoder side (issue 7)
+ Handle unterminated strings when encoding (issue 68)
+ Fix bug with empty strings in repeated string callbacks (issue 73)
+ Fix regression in 0.2.0 with optional callback fields (issue 70)
+ Fix bugs with empty message types (issues 64, 65)
+ Fix some compiler warnings on clang (issue 67)
+ Some portability improvements (issues 60, 62)
+ Various new generator options
+ Improved tests
+
+nanopb-0.2.0 (2013-03-02)
+ NOTE: This release requires you to regenerate all .pb.c
+       files. Files generated by older versions will not
+       compile anymore.
+
+ Reformat generated .pb.c files using macros (issue 58)
+ Rename PB_HTYPE_ARRAY -> PB_HTYPE_REPEATED
+ Separate PB_HTYPE to PB_ATYPE and PB_HTYPE
+ Move STATIC_ASSERTs to .pb.c file
+ Added CMake file (by Pavel Ilin)
+ Add option to give file extension to generator (by Michael Haberler)
+ Documentation updates
+
+nanopb-0.1.9 (2013-02-13)
+ Fixed error message bugs (issues 52, 56)
+ Sanitize #ifndef filename (issue 50)
+ Performance improvements
+ Add compile-time option PB_BUFFER_ONLY
+ Add Java package name to nanopb.proto
+ Check for sizeof(double) == 8 (issue 54)
+ Added generator option to ignore some fields. (issue 51)
+ Added generator option to make message structs packed. (issue 49)
+ Add more test cases.
+
+nanopb-0.1.8 (2012-12-13)
+ Fix bugs in the enum short names introduced in 0.1.7 (issues 42, 43)
+ Fix STATIC_ASSERT macro when using multiple .proto files. (issue 41)
+ Fix missing initialization of istream.errmsg
+ Make tests/Makefile work for non-gcc compilers (issue 40)
+
+nanopb-0.1.7 (2012-11-11)
+ Remove "skip" mode from pb_istream_t callbacks. Example implementation had a bug. (issue 37)
+ Add option to use shorter names for enum values (issue 38)
+ Improve options support in generator (issues 12, 30)
+ Add nanopb version number to generated files (issue 36)
+ Add extern "C" to generated headers (issue 35)
+ Add names for structs to allow forward declaration (issue 39)
+ Add buffer size check in example (issue 34)
+ Fix build warnings on MS compilers (issue 33)
+
+nanopb-0.1.6 (2012-09-02)
+ Reorganize the field decoder interface (issue 2)
+ Improve performance in submessage decoding (issue 28)
+ Implement error messages in the decoder side (issue 7)
+ Extended testcases (alltypes test is now complete).
+ Fix some compiler warnings (issues 25, 26, 27, 32).
+
+nanopb-0.1.5 (2012-08-04)
+ Fix bug in decoder with packed arrays (issue 23).
+ Extended testcases.
+ Fix some compiler warnings.
+
+nanopb-0.1.4 (2012-07-05)
+ Add compile-time options for easy-to-use >255 field support.
+ Improve the detection of missing required fields.
+ Added example on how to handle union messages.
+ Fix generator error with .proto without messages.
+ Fix problems that stopped the code from compiling with some compilers.
+ Fix some compiler warnings.
+
+nanopb-0.1.3 (2012-06-12)
+ Refactor the field encoder interface.
+ Improve generator error messages (issue 5)
+ Add descriptor.proto into the #include exclusion list
+ Fix some compiler warnings.
+
+nanopb-0.1.2 (2012-02-15)
+ Make the generator to generate include for other .proto files (issue 4).
+ Fixed generator not working on Windows (issue 3)
+
+nanopb-0.1.1 (2012-01-14)
+ Fixed bug in encoder with 'bytes' fields (issue 1).
+ Fixed a bug in the generator that caused a compiler error on sfixed32 and sfixed64 fields.
+ Extended testcases.
+
+nanopb-0.1.0 (2012-01-06)
+ First stable release.
diff --git a/third_party/nanopb/CONTRIBUTING.md b/third_party/nanopb/CONTRIBUTING.md
new file mode 100644
index 0000000..4041bc3
--- /dev/null
+++ b/third_party/nanopb/CONTRIBUTING.md
@@ -0,0 +1,32 @@
+Contributing to Nanopb development
+==================================
+
+Reporting issues and requesting features
+----------------------------------------
+
+Feel free to report any issues you see or features you would like
+to see in the future to the Github issue tracker. Using the templates
+below is preferred:
+
+* [Report a bug](https://github.com/nanopb/nanopb/issues/new?body=**Steps%20to%20reproduce%20the%20issue**%0a%0a1.%0a2.%0a3.%0a%0a**What%20happens?**%0A%0A**What%20should%20happen?**&labels=Type-Defect)
+* [Request a feature](https://github.com/nanopb/nanopb/issues/new?body=**What%20should%20the%20feature%20do?**%0A%0A**In%20what%20situation%20would%20the%20feature%20be%20useful?**&labels=Type-Enhancement)
+
+Requesting help
+---------------
+
+If there is something strange going on, but you do not know if
+it is actually a bug in nanopb, try asking first on the
+[discussion forum](https://groups.google.com/forum/#!forum/nanopb).
+
+Pull requests
+-------------
+
+Pull requests are welcome!
+
+If it is not obvious from the commit message, please indicate the
+same information as you would for an issue report:
+
+* What functionality it fixes/adds.
+* How can the problem be reproduced / when would the feature be useful.
+
+
diff --git a/third_party/nanopb/LICENSE.txt b/third_party/nanopb/LICENSE.txt
new file mode 100644
index 0000000..d11c9af
--- /dev/null
+++ b/third_party/nanopb/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2011 Petteri Aimonen <jpa at nanopb.mail.kapsi.fi>
+
+This software is provided 'as-is', without any express or 
+implied warranty. In no event will the authors be held liable 
+for any damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any 
+purpose, including commercial applications, and to alter it and 
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you 
+   must not claim that you wrote the original software. If you use 
+   this software in a product, an acknowledgment in the product 
+   documentation would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and 
+   must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source 
+   distribution.
diff --git a/third_party/nanopb/README.md b/third_party/nanopb/README.md
new file mode 100644
index 0000000..2d35e85
--- /dev/null
+++ b/third_party/nanopb/README.md
@@ -0,0 +1,71 @@
+Nanopb - Protocol Buffers for Embedded Systems
+==============================================
+
+[![Build Status](https://travis-ci.org/nanopb/nanopb.svg?branch=master)](https://travis-ci.org/nanopb/nanopb)
+
+Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is
+especially suitable for use in microcontrollers, but fits any memory
+restricted system.
+
+* **Homepage:** http://kapsi.fi/~jpa/nanopb/
+* **Documentation:** http://kapsi.fi/~jpa/nanopb/docs/
+* **Downloads:** http://kapsi.fi/~jpa/nanopb/download/
+* **Forum:** https://groups.google.com/forum/#!forum/nanopb
+
+
+
+Using the nanopb library
+------------------------
+To use the nanopb library, you need to do two things:
+
+1. Compile your .proto files for nanopb, using protoc.
+2. Include pb_encode.c and pb_decode.c in your project.
+
+The easiest way to get started is to study the project in "examples/simple".
+It contains a Makefile, which should work directly under most Linux systems.
+However, for any other kind of build system, see the manual steps in
+README.txt in that folder.
+
+
+
+Using the Protocol Buffers compiler (protoc)
+--------------------------------------------
+The nanopb generator is implemented as a plugin for the Google's own protoc
+compiler. This has the advantage that there is no need to reimplement the
+basic parsing of .proto files. However, it does mean that you need the
+Google's protobuf library in order to run the generator.
+
+If you have downloaded a binary package for nanopb (either Windows, Linux or
+Mac OS X version), the 'protoc' binary is included in the 'generator-bin'
+folder. In this case, you are ready to go. Simply run this command:
+
+    generator-bin/protoc --nanopb_out=. myprotocol.proto
+
+However, if you are using a git checkout or a plain source distribution, you
+need to provide your own version of protoc and the Google's protobuf library.
+On Linux, the necessary packages are protobuf-compiler and python-protobuf.
+On Windows, you can either build Google's protobuf library from source or use
+one of the binary distributions of it. In either case, if you use a separate
+protoc, you need to manually give the path to nanopb generator:
+
+    protoc --plugin=protoc-gen-nanopb=nanopb/generator/protoc-gen-nanopb ...
+
+
+
+Running the tests
+-----------------
+If you want to perform further development of the nanopb core, or to verify
+its functionality using your compiler and platform, you'll want to run the
+test suite. The build rules for the test suite are implemented using Scons,
+so you need to have that installed. To run the tests:
+
+    cd tests
+    scons
+
+This will show the progress of various test cases. If the output does not
+end in an error, the test cases were successful.
+
+Note: Mac OS X by default aliases 'clang' as 'gcc', while not actually
+supporting the same command line options as gcc does. To run tests on
+Mac OS X, use: "scons CC=clang CXX=clang". Same way can be used to run
+tests with different compilers on any platform.
diff --git a/third_party/nanopb/docs/Makefile b/third_party/nanopb/docs/Makefile
new file mode 100644
index 0000000..0dbd97c
--- /dev/null
+++ b/third_party/nanopb/docs/Makefile
@@ -0,0 +1,9 @@
+all: index.html concepts.html reference.html security.html migration.html \
+	generator_flow.png
+
+%.png: %.svg
+	rsvg $< $@
+
+%.html: %.rst
+	rst2html --stylesheet=lsr.css --link-stylesheet $< $@
+	sed -i 's!</head>!<link href="favicon.ico" type="image/x-icon" rel="shortcut icon" />\n</head>!' $@
diff --git a/third_party/nanopb/docs/concepts.rst b/third_party/nanopb/docs/concepts.rst
new file mode 100644
index 0000000..c43d829
--- /dev/null
+++ b/third_party/nanopb/docs/concepts.rst
@@ -0,0 +1,392 @@
+======================
+Nanopb: Basic concepts
+======================
+
+.. include :: menu.rst
+
+The things outlined here are the underlying concepts of the nanopb design.
+
+.. contents::
+
+Proto files
+===========
+All Protocol Buffers implementations use .proto files to describe the message
+format. The point of these files is to be a portable interface description
+language.
+
+Compiling .proto files for nanopb
+---------------------------------
+Nanopb uses the Google's protoc compiler to parse the .proto file, and then a
+python script to generate the C header and source code from it::
+
+    user@host:~$ protoc -omessage.pb message.proto
+    user@host:~$ python ../generator/nanopb_generator.py message.pb
+    Writing to message.h and message.c
+    user@host:~$
+
+Modifying generator behaviour
+-----------------------------
+Using generator options, you can set maximum sizes for fields in order to
+allocate them statically. The preferred way to do this is to create an .options
+file with the same name as your .proto file::
+
+   # Foo.proto
+   message Foo {
+      required string name = 1;
+   }
+
+::
+
+   # Foo.options
+   Foo.name max_size:16
+
+For more information on this, see the `Proto file options`_ section in the
+reference manual.
+
+.. _`Proto file options`: reference.html#proto-file-options
+
+Streams
+=======
+
+Nanopb uses streams for accessing the data in encoded format.
+The stream abstraction is very lightweight, and consists of a structure (*pb_ostream_t* or *pb_istream_t*) which contains a pointer to a callback function.
+
+There are a few generic rules for callback functions:
+
+#) Return false on IO errors. The encoding or decoding process will abort immediately.
+#) Use state to store your own data, such as a file descriptor.
+#) *bytes_written* and *bytes_left* are updated by pb_write and pb_read.
+#) Your callback may be used with substreams. In this case *bytes_left*, *bytes_written* and *max_size* have smaller values than the original stream. Don't use these values to calculate pointers.
+#) Always read or write the full requested length of data. For example, POSIX *recv()* needs the *MSG_WAITALL* parameter to accomplish this.
+
+Output streams
+--------------
+
+::
+
+ struct _pb_ostream_t
+ {
+    bool (*callback)(pb_ostream_t *stream, const uint8_t *buf, size_t count);
+    void *state;
+    size_t max_size;
+    size_t bytes_written;
+ };
+
+The *callback* for output stream may be NULL, in which case the stream simply counts the number of bytes written. In this case, *max_size* is ignored.
+
+Otherwise, if *bytes_written* + bytes_to_be_written is larger than *max_size*, pb_write returns false before doing anything else. If you don't want to limit the size of the stream, pass SIZE_MAX.
+ 
+**Example 1:**
+
+This is the way to get the size of the message without storing it anywhere::
+
+ Person myperson = ...;
+ pb_ostream_t sizestream = {0};
+ pb_encode(&sizestream, Person_fields, &myperson);
+ printf("Encoded size is %d\n", sizestream.bytes_written);
+
+**Example 2:**
+
+Writing to stdout::
+
+ bool callback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
+ {
+    FILE *file = (FILE*) stream->state;
+    return fwrite(buf, 1, count, file) == count;
+ }
+ 
+ pb_ostream_t stdoutstream = {&callback, stdout, SIZE_MAX, 0};
+
+Input streams
+-------------
+For input streams, there is one extra rule:
+
+#) You don't need to know the length of the message in advance. After getting EOF error when reading, set bytes_left to 0 and return false. Pb_decode will detect this and if the EOF was in a proper position, it will return true.
+
+Here is the structure::
+
+ struct _pb_istream_t
+ {
+    bool (*callback)(pb_istream_t *stream, uint8_t *buf, size_t count);
+    void *state;
+    size_t bytes_left;
+ };
+
+The *callback* must always be a function pointer. *Bytes_left* is an upper limit on the number of bytes that will be read. You can use SIZE_MAX if your callback handles EOF as described above.
+
+**Example:**
+
+This function binds an input stream to stdin:
+
+:: 
+
+ bool callback(pb_istream_t *stream, uint8_t *buf, size_t count)
+ {
+    FILE *file = (FILE*)stream->state;
+    bool status;
+    
+    if (buf == NULL)
+    {
+        while (count-- && fgetc(file) != EOF);
+        return count == 0;
+    }
+    
+    status = (fread(buf, 1, count, file) == count);
+    
+    if (feof(file))
+        stream->bytes_left = 0;
+    
+    return status;
+ }
+ 
+ pb_istream_t stdinstream = {&callback, stdin, SIZE_MAX};
+
+Data types
+==========
+
+Most Protocol Buffers datatypes have directly corresponding C datatypes, such as int32 is int32_t, float is float and bool is bool. However, the variable-length datatypes are more complex:
+
+1) Strings, bytes and repeated fields of any type map to callback functions by default.
+2) If there is a special option *(nanopb).max_size* specified in the .proto file, string maps to null-terminated char array and bytes map to a structure containing a char array and a size field.
+3) If *(nanopb).type* is set to *FT_INLINE* and *(nanopb).max_size* is also set, then bytes map to an inline byte array of fixed size.
+3) If there is a special option *(nanopb).max_count* specified on a repeated field, it maps to an array of whatever type is being repeated. Another field will be created for the actual number of entries stored.
+
+=============================================================================== =======================
+      field in .proto                                                           autogenerated in .h
+=============================================================================== =======================
+required string name = 1;                                                       pb_callback_t name;
+required string name = 1 [(nanopb).max_size = 40];                              char name[40];
+repeated string name = 1 [(nanopb).max_size = 40];                              pb_callback_t name;
+repeated string name = 1 [(nanopb).max_size = 40, (nanopb).max_count = 5];      | size_t name_count;
+                                                                                | char name[5][40];
+required bytes data = 1 [(nanopb).max_size = 40];                               | typedef struct {
+                                                                                |    size_t size;
+                                                                                |    pb_byte_t bytes[40];
+                                                                                | } Person_data_t;
+                                                                                | Person_data_t data;
+required bytes data = 1 [(nanopb).max_size = 40, (nanopb.type) = FT_INLINE];    | pb_byte_t data[40];
+=============================================================================== =======================
+
+The maximum lengths are checked in runtime. If string/bytes/array exceeds the allocated length, *pb_decode* will return false.
+
+Note: for the *bytes* datatype, the field length checking may not be exact.
+The compiler may add some padding to the *pb_bytes_t* structure, and the nanopb runtime doesn't know how much of the structure size is padding. Therefore it uses the whole length of the structure for storing data, which is not very smart but shouldn't cause problems. In practise, this means that if you specify *(nanopb).max_size=5* on a *bytes* field, you may be able to store 6 bytes there. For the *string* field type, the length limit is exact.
+
+Field callbacks
+===============
+When a field has dynamic length, nanopb cannot statically allocate storage for it. Instead, it allows you to handle the field in whatever way you want, using a callback function.
+
+The `pb_callback_t`_ structure contains a function pointer and a *void* pointer called *arg* you can use for passing data to the callback. If the function pointer is NULL, the field will be skipped. A pointer to the *arg* is passed to the function, so that it can modify it and retrieve the value.
+
+The actual behavior of the callback function is different in encoding and decoding modes. In encoding mode, the callback is called once and should write out everything, including field tags. In decoding mode, the callback is called repeatedly for every data item.
+
+.. _`pb_callback_t`: reference.html#pb-callback-t
+
+Encoding callbacks
+------------------
+::
+
+    bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
+
+When encoding, the callback should write out complete fields, including the wire type and field number tag. It can write as many or as few fields as it likes. For example, if you want to write out an array as *repeated* field, you should do it all in a single call.
+
+Usually you can use `pb_encode_tag_for_field`_ to encode the wire type and tag number of the field. However, if you want to encode a repeated field as a packed array, you must call `pb_encode_tag`_ instead to specify a wire type of *PB_WT_STRING*.
+
+If the callback is used in a submessage, it will be called multiple times during a single call to `pb_encode`_. In this case, it must produce the same amount of data every time. If the callback is directly in the main message, it is called only once.
+
+.. _`pb_encode`: reference.html#pb-encode
+.. _`pb_encode_tag_for_field`: reference.html#pb-encode-tag-for-field
+.. _`pb_encode_tag`: reference.html#pb-encode-tag
+
+This callback writes out a dynamically sized string::
+
+    bool write_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+    {
+        char *str = get_string_from_somewhere();
+        if (!pb_encode_tag_for_field(stream, field))
+            return false;
+        
+        return pb_encode_string(stream, (uint8_t*)str, strlen(str));
+    }
+
+Decoding callbacks
+------------------
+::
+
+    bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
+
+When decoding, the callback receives a length-limited substring that reads the contents of a single field. The field tag has already been read. For *string* and *bytes*, the length value has already been parsed, and is available at *stream->bytes_left*.
+
+The callback will be called multiple times for repeated fields. For packed fields, you can either read multiple values until the stream ends, or leave it to `pb_decode`_ to call your function over and over until all values have been read.
+
+.. _`pb_decode`: reference.html#pb-decode
+
+This callback reads multiple integers and prints them::
+
+    bool read_ints(pb_istream_t *stream, const pb_field_t *field, void **arg)
+    {
+        while (stream->bytes_left)
+        {
+            uint64_t value;
+            if (!pb_decode_varint(stream, &value))
+                return false;
+            printf("%lld\n", value);
+        }
+        return true;
+    }
+
+Field description array
+=======================
+
+For using the *pb_encode* and *pb_decode* functions, you need an array of pb_field_t constants describing the structure you wish to encode. This description is usually autogenerated from .proto file.
+
+For example this submessage in the Person.proto file::
+
+ message Person {
+    message PhoneNumber {
+        required string number = 1 [(nanopb).max_size = 40];
+        optional PhoneType type = 2 [default = HOME];
+    }
+ }
+
+generates this field description array for the structure *Person_PhoneNumber*::
+
+ const pb_field_t Person_PhoneNumber_fields[3] = {
+    PB_FIELD(  1, STRING  , REQUIRED, STATIC, Person_PhoneNumber, number, number, 0),
+    PB_FIELD(  2, ENUM    , OPTIONAL, STATIC, Person_PhoneNumber, type, number, &Person_PhoneNumber_type_default),
+    PB_LAST_FIELD
+ };
+
+Oneof
+=====
+Protocol Buffers supports `oneof`_ sections. Here is an example of ``oneof`` usage::
+
+ message MsgType1 {
+     required int32 value = 1;
+ }
+
+ message MsgType2 {
+     required bool value = 1;
+ }
+ 
+ message MsgType3 {
+     required int32 value1 = 1;
+     required int32 value2 = 2;
+ } 
+ 
+ message MyMessage {
+     required uint32 uid = 1;
+     required uint32 pid = 2;
+     required uint32 utime = 3;
+ 
+     oneof payload {
+         MsgType1 msg1 = 4;
+         MsgType2 msg2 = 5;
+         MsgType3 msg3 = 6;
+     }
+ }
+
+Nanopb will generate ``payload`` as a C union and add an additional field ``which_payload``::
+
+  typedef struct _MyMessage {
+    uint32_t uid;
+    uint32_t pid;
+    uint32_t utime;
+    pb_size_t which_payload;
+    union {
+        MsgType1 msg1;
+        MsgType2 msg2;
+        MsgType3 msg3;
+    } payload;
+  /* @@protoc_insertion_point(struct:MyMessage) */
+  } MyMessage;
+
+``which_payload`` indicates which of the ``oneof`` fields is actually set. 
+The user is expected to set the filed manually using the correct field tag::
+
+  MyMessage msg = MyMessage_init_zero;
+  msg.payload.msg2.value = true;
+  msg.which_payload = MyMessage_msg2_tag;
+
+Notice that neither ``which_payload`` field nor the unused fileds in ``payload``
+will consume any space in the resulting encoded message.
+
+.. _`oneof`: https://developers.google.com/protocol-buffers/docs/reference/proto2-spec#oneof_and_oneof_field
+
+Extension fields
+================
+Protocol Buffers supports a concept of `extension fields`_, which are
+additional fields to a message, but defined outside the actual message.
+The definition can even be in a completely separate .proto file.
+
+The base message is declared as extensible by keyword *extensions* in
+the .proto file::
+
+ message MyMessage {
+     .. fields ..
+     extensions 100 to 199;
+ }
+
+For each extensible message, *nanopb_generator.py* declares an additional
+callback field called *extensions*. The field and associated datatype
+*pb_extension_t* forms a linked list of handlers. When an unknown field is
+encountered, the decoder calls each handler in turn until either one of them
+handles the field, or the list is exhausted.
+
+The actual extensions are declared using the *extend* keyword in the .proto,
+and are in the global namespace::
+
+ extend MyMessage {
+     optional int32 myextension = 100;
+ }
+
+For each extension, *nanopb_generator.py* creates a constant of type
+*pb_extension_type_t*. To link together the base message and the extension,
+you have to:
+
+1. Allocate storage for your field, matching the datatype in the .proto.
+   For example, for a *int32* field, you need a *int32_t* variable to store
+   the value.
+2. Create a *pb_extension_t* constant, with pointers to your variable and
+   to the generated *pb_extension_type_t*.
+3. Set the *message.extensions* pointer to point to the *pb_extension_t*.
+
+An example of this is available in *tests/test_encode_extensions.c* and
+*tests/test_decode_extensions.c*.
+
+.. _`extension fields`: https://developers.google.com/protocol-buffers/docs/proto#extensions
+
+Message framing
+===============
+Protocol Buffers does not specify a method of framing the messages for transmission.
+This is something that must be provided by the library user, as there is no one-size-fits-all
+solution. Typical needs for a framing format are to:
+
+1. Encode the message length.
+2. Encode the message type.
+3. Perform any synchronization and error checking that may be needed depending on application.
+
+For example UDP packets already fullfill all the requirements, and TCP streams typically only
+need a way to identify the message length and type. Lower level interfaces such as serial ports
+may need a more robust frame format, such as HDLC (high-level data link control).
+
+Nanopb provides a few helpers to facilitate implementing framing formats:
+
+1. Functions *pb_encode_delimited* and *pb_decode_delimited* prefix the message data with a varint-encoded length.
+2. Union messages and oneofs are supported in order to implement top-level container messages.
+3. Message IDs can be specified using the *(nanopb_msgopt).msgid* option and can then be accessed from the header.
+
+Return values and error handling
+================================
+
+Most functions in nanopb return bool: *true* means success, *false* means failure. There is also some support for error messages for debugging purposes: the error messages go in *stream->errmsg*.
+
+The error messages help in guessing what is the underlying cause of the error. The most common error conditions are:
+
+1) Running out of memory, i.e. stack overflow.
+2) Invalid field descriptors (would usually mean a bug in the generator).
+3) IO errors in your own stream callbacks.
+4) Errors that happen in your callback functions.
+5) Exceeding the max_size or bytes_left of a stream.
+6) Exceeding the max_size of a string or array field
+7) Invalid protocol buffers binary message.
diff --git a/third_party/nanopb/docs/generator_flow.svg b/third_party/nanopb/docs/generator_flow.svg
new file mode 100644
index 0000000..e30277a
--- /dev/null
+++ b/third_party/nanopb/docs/generator_flow.svg
@@ -0,0 +1,2869 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="490"
+   height="220"
+   id="svg1901"
+   sodipodi:version="0.32"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="generator_flow.svg"
+   version="1.1">
+  <defs
+     id="defs1903">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient15568">
+      <stop
+         style="stop-color:#729fcf;stop-opacity:1;"
+         offset="0"
+         id="stop15570" />
+      <stop
+         style="stop-color:#729fcf;stop-opacity:0;"
+         offset="1"
+         id="stop15572" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient15558">
+      <stop
+         style="stop-color:#fcaf3e;stop-opacity:1;"
+         offset="0"
+         id="stop15560" />
+      <stop
+         style="stop-color:#fcaf3e;stop-opacity:0;"
+         offset="1"
+         id="stop15562" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient13629">
+      <stop
+         style="stop-color:#fce94f;stop-opacity:1;"
+         offset="0"
+         id="stop13631" />
+      <stop
+         style="stop-color:#fce94f;stop-opacity:0;"
+         offset="1"
+         id="stop13633" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.284916,0,30.08928)"
+       r="15.821514"
+       fy="42.07798"
+       fx="24.306795"
+       cy="42.07798"
+       cx="24.306795"
+       id="radialGradient4548"
+       xlink:href="#linearGradient4542"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient259">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop260" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop261" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop270" />
+      <stop
+         style="stop-color:#4c4c4c;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop271" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       fy="114.5684"
+       fx="20.892099"
+       r="5.256"
+       cy="114.5684"
+       cx="20.892099"
+       id="aigrd2">
+      <stop
+         id="stop15566"
+         style="stop-color:#F0F0F0"
+         offset="0" />
+      <stop
+         id="stop15568"
+         style="stop-color:#9a9a9a;stop-opacity:1.0000000;"
+         offset="1.0000000" />
+    </radialGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       fy="64.567902"
+       fx="20.892099"
+       r="5.257"
+       cy="64.567902"
+       cx="20.892099"
+       id="aigrd3">
+      <stop
+         id="stop15573"
+         style="stop-color:#F0F0F0"
+         offset="0" />
+      <stop
+         id="stop15575"
+         style="stop-color:#9a9a9a;stop-opacity:1.0000000;"
+         offset="1.0000000" />
+    </radialGradient>
+    <linearGradient
+       id="linearGradient15662">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop15664" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop15666" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4542"
+       inkscape:collect="always">
+      <stop
+         id="stop4544"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         id="stop4546"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5048">
+      <stop
+         id="stop5050"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop5056" />
+      <stop
+         id="stop5052"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5060"
+       inkscape:collect="always">
+      <stop
+         id="stop5062"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop5064"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="9.586297"
+       fy="9.8105707"
+       fx="21.578989"
+       cy="9.8105707"
+       cx="21.578989"
+       gradientTransform="matrix(0.74942,0,0,0.394055,6.226925,10.09253)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2908"
+       xlink:href="#linearGradient2869"
+       inkscape:collect="always" />
+    <radialGradient
+       r="6.4286141"
+       fy="20.800287"
+       fx="23.94367"
+       cy="20.800287"
+       cx="23.94367"
+       gradientTransform="matrix(1.353283,0,0,0.635968,-8.45889,3.41347)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2906"
+       xlink:href="#linearGradient2884"
+       inkscape:collect="always" />
+    <radialGradient
+       r="9.586297"
+       fy="9.0255041"
+       fx="21.578989"
+       cy="9.0255041"
+       cx="21.578989"
+       gradientTransform="matrix(0.74942,0,0,0.394055,6.226925,10.09253)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2898"
+       xlink:href="#linearGradient2869"
+       inkscape:collect="always" />
+    <radialGradient
+       r="6.4286141"
+       fy="20.800287"
+       fx="23.94367"
+       cy="20.800287"
+       cx="23.94367"
+       gradientTransform="matrix(1.353283,0,0,0.635968,-8.45889,3.41347)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2896"
+       xlink:href="#linearGradient2884"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="22.585211"
+       x2="24.990499"
+       y1="34.004856"
+       x1="24.990499"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2288"
+       xlink:href="#linearGradient4210"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="47.388485"
+       x2="30.014812"
+       y1="19.912336"
+       x1="18.706615"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2285"
+       xlink:href="#linearGradient4222"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="16.020695"
+       x2="22.071806"
+       y1="9.7577486"
+       x1="21.906841"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2282"
+       xlink:href="#linearGradient4987"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="12.636667"
+       x2="34.193642"
+       y1="12.636667"
+       x1="16.148972"
+       gradientTransform="matrix(1,0,0,1.039184,0,-0.04057054)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2280"
+       xlink:href="#linearGradient4182"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="16.17037"
+       x2="24.119167"
+       y1="24.720648"
+       x1="25.381256"
+       gradientTransform="matrix(1,0,0,0.986355,0,0.316638)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2277"
+       xlink:href="#linearGradient4192"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="15.267649"
+       x2="47.065834"
+       y1="14.661557"
+       x1="36.288929"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2274"
+       xlink:href="#linearGradient4995"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="12.333632"
+       x2="17.696169"
+       y1="13.444801"
+       x1="30.062469"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2269"
+       xlink:href="#linearGradient4979"
+       inkscape:collect="always" />
+    <radialGradient
+       r="17.576654"
+       fy="35.373093"
+       fx="22.930462"
+       cy="35.373093"
+       cx="22.930462"
+       gradientTransform="matrix(1,0,0,0.333333,0,23.58206)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2252"
+       xlink:href="#linearGradient4946"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4182">
+      <stop
+         style="stop-color:#a36d18;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4184" />
+      <stop
+         style="stop-color:#d79020;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4186" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4192">
+      <stop
+         style="stop-color:#e9b96e;stop-opacity:1;"
+         offset="0"
+         id="stop4194" />
+      <stop
+         style="stop-color:#f1d19e;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4196" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4210">
+      <stop
+         style="stop-color:#eaba6f;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4212" />
+      <stop
+         style="stop-color:#b97a1b;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4214" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4222">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop4224" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0.68639052;"
+         offset="1.0000000"
+         id="stop4226" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4946">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4948" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4950" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4979">
+      <stop
+         style="stop-color:#fbf0e0;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4981" />
+      <stop
+         style="stop-color:#f0ce99;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4983" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4987">
+      <stop
+         style="stop-color:#a0670c;stop-opacity:1;"
+         offset="0"
+         id="stop4989" />
+      <stop
+         style="stop-color:#a0670c;stop-opacity:0;"
+         offset="1"
+         id="stop4991" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4995">
+      <stop
+         style="stop-color:#de9523;stop-opacity:1;"
+         offset="0"
+         id="stop4997" />
+      <stop
+         style="stop-color:#a36d18;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4999" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2869">
+      <stop
+         id="stop2871"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop2873"
+         offset="1.0000000"
+         style="stop-color:#cccccc;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2884"
+       inkscape:collect="always">
+      <stop
+         id="stop2886"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         id="stop2888"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient6715-6"
+       xlink:href="#linearGradient5048-7"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5048-7">
+      <stop
+         id="stop5050-2"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop5056-4" />
+      <stop
+         id="stop5052-0"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient6717-4"
+       xlink:href="#linearGradient5060-6"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5060-6"
+       inkscape:collect="always">
+      <stop
+         id="stop5062-2"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop5064-8"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient6719-8"
+       xlink:href="#linearGradient5060-6"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientTransform="matrix(-0.901805,0.210818,-0.211618,-0.898788,63.54132,37.87423)"
+       y2="46.06208"
+       x2="29.477814"
+       y1="2.8703361"
+       x1="25.950134"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5486"
+       xlink:href="#linearGradient5476"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientTransform="matrix(1.106909,-0.258404,0.259748,1.101665,-19.66697,12.19788)"
+       gradientUnits="userSpaceOnUse"
+       y2="46.06208"
+       x2="29.477814"
+       y1="2.8703361"
+       x1="25.950134"
+       id="linearGradient5482"
+       xlink:href="#linearGradient5476"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientTransform="matrix(1.106909,-0.258404,0.259748,1.101665,-19.66697,12.19788)"
+       y2="2.8163671"
+       x2="21.587093"
+       y1="23.499001"
+       x1="21.587093"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5472"
+       xlink:href="#linearGradient5464"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4356">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4358" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4360" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4344">
+      <stop
+         style="stop-color:#727e0a;stop-opacity:1;"
+         offset="0"
+         id="stop4346" />
+      <stop
+         style="stop-color:#5b6508;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4348" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4338">
+      <stop
+         id="stop4340"
+         offset="0.0000000"
+         style="stop-color:#e9b15e;stop-opacity:1.0000000;" />
+      <stop
+         id="stop4342"
+         offset="1.0000000"
+         style="stop-color:#966416;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4163">
+      <stop
+         style="stop-color:#3b74bc;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop4165" />
+      <stop
+         style="stop-color:#2d5990;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4167" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3824">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3826" />
+      <stop
+         style="stop-color:#c9c9c9;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop3828" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3816">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop3818" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop3820" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3800">
+      <stop
+         style="stop-color:#f4d9b1;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop3802" />
+      <stop
+         style="stop-color:#df9725;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop3804" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3800"
+       id="radialGradient3806"
+       cx="29.344931"
+       cy="17.064077"
+       fx="29.344931"
+       fy="17.064077"
+       r="9.1620579"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3816"
+       id="radialGradient3822"
+       cx="31.112698"
+       cy="19.008621"
+       fx="31.112698"
+       fy="19.008621"
+       r="8.6620579"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3824"
+       id="linearGradient3830"
+       x1="30.935921"
+       y1="29.553486"
+       x2="30.935921"
+       y2="35.803486"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4163"
+       id="radialGradient4169"
+       cx="28.089741"
+       cy="27.203083"
+       fx="28.089741"
+       fy="27.203083"
+       r="13.56536"
+       gradientTransform="matrix(1.297564,0,0,0.884831,-8.358505,4.940469)"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3800"
+       id="radialGradient4171"
+       gradientUnits="userSpaceOnUse"
+       cx="29.344931"
+       cy="17.064077"
+       fx="29.344931"
+       fy="17.064077"
+       r="9.1620579"
+       gradientTransform="matrix(0.787998,0,0,0.787998,6.221198,3.617627)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3824"
+       id="linearGradient4175"
+       gradientUnits="userSpaceOnUse"
+       x1="30.935921"
+       y1="29.553486"
+       x2="30.935921"
+       y2="35.803486"
+       gradientTransform="translate(0.707108,0)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3816"
+       id="radialGradient4179"
+       gradientUnits="userSpaceOnUse"
+       cx="31.112698"
+       cy="19.008621"
+       fx="31.112698"
+       fy="19.008621"
+       r="8.6620579" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3824"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-12.41789,-7)"
+       x1="30.935921"
+       y1="29.553486"
+       x2="30.935921"
+       y2="35.803486" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4338"
+       id="radialGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.787998,0,0,0.787998,6.221198,3.617627)"
+       cx="29.344931"
+       cy="17.064077"
+       fx="29.344931"
+       fy="17.064077"
+       r="9.1620579" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3816"
+       id="radialGradient4330"
+       gradientUnits="userSpaceOnUse"
+       cx="31.112698"
+       cy="19.008621"
+       fx="31.112698"
+       fy="19.008621"
+       r="8.6620579" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3824"
+       id="linearGradient4332"
+       gradientUnits="userSpaceOnUse"
+       x1="30.935921"
+       y1="29.553486"
+       x2="30.935921"
+       y2="35.803486"
+       gradientTransform="translate(-13.125,-7)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3816"
+       id="radialGradient4336"
+       gradientUnits="userSpaceOnUse"
+       cx="31.112698"
+       cy="19.008621"
+       fx="31.112698"
+       fy="19.008621"
+       r="8.6620579" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4344"
+       id="radialGradient4350"
+       cx="16.214741"
+       cy="19.836468"
+       fx="16.214741"
+       fy="19.836468"
+       r="13.56536"
+       gradientTransform="matrix(1,0,0,0.681917,0,8.233773)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4356"
+       id="linearGradient4362"
+       x1="20.661695"
+       y1="35.817974"
+       x2="22.626925"
+       y2="36.217758"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.983375,0.181588,-0.181588,0.983375,6.231716,-2.651466)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4356"
+       id="linearGradient4366"
+       gradientUnits="userSpaceOnUse"
+       x1="22.686766"
+       y1="36.3904"
+       x2="21.408455"
+       y2="35.739632"
+       gradientTransform="matrix(-0.977685,0.210075,0.210075,0.977685,55.1096,-3.945209)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4356"
+       id="linearGradient4372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.983375,0.181588,-0.181588,0.983375,-7.07212,-9.82492)"
+       x1="20.661695"
+       y1="35.817974"
+       x2="22.626925"
+       y2="36.217758" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4356"
+       id="linearGradient4374"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.977685,0.210075,0.210075,0.977685,41.80576,-11.11866)"
+       x1="22.686766"
+       y1="36.3904"
+       x2="21.408455"
+       y2="35.739632" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4356"
+       id="linearGradient1366"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.977685,0.210075,0.210075,0.977685,41.80576,-11.11866)"
+       x1="22.686766"
+       y1="36.3904"
+       x2="21.408455"
+       y2="35.739632" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4356"
+       id="linearGradient1369"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.983375,0.181588,-0.181588,0.983375,-7.07212,-9.82492)"
+       x1="20.661695"
+       y1="35.817974"
+       x2="22.626925"
+       y2="36.217758" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3824"
+       id="linearGradient1372"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-12.41789,-7)"
+       x1="30.935921"
+       y1="29.553486"
+       x2="30.935921"
+       y2="35.803486" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4344"
+       id="radialGradient1381"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.681917,0,8.233773)"
+       cx="16.214741"
+       cy="19.836468"
+       fx="16.214741"
+       fy="19.836468"
+       r="13.56536" />
+    <linearGradient
+       y2="11.981981"
+       x2="13.846983"
+       y1="11.48487"
+       x1="11.74217"
+       gradientTransform="matrix(1.276531,0,0,-1.406115,24.24763,33.3374)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient1493"
+       xlink:href="#linearGradient15107"
+       inkscape:collect="always" />
+    <radialGradient
+       r="7.228416"
+       fy="73.615715"
+       fx="6.702713"
+       cy="73.615715"
+       cx="6.702713"
+       gradientTransform="scale(1.902215,0.525703)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient1481"
+       xlink:href="#linearGradient10691"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="12.765438"
+       x2="38.129341"
+       y1="7.7850504"
+       x1="26.577936"
+       gradientTransform="matrix(0,1,-1,0,37.07553,-5.879343)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2365"
+       xlink:href="#linearGradient4274"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="29.698416"
+       x2="16.588747"
+       y1="16.612858"
+       x1="41.093174"
+       gradientTransform="matrix(0,0.914114,-0.914114,0,39.78243,-9.748047)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2566"
+       xlink:href="#linearGradient2187"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="27.145725"
+       x2="10.112462"
+       y1="23.332331"
+       x1="10.791593"
+       gradientTransform="matrix(0,1,-1,0,37.07553,-5.879343)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2564"
+       xlink:href="#linearGradient6925"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="30.55784"
+       x2="12.252101"
+       y1="15.028743"
+       x1="15.193591"
+       gradientTransform="matrix(0,1,-1,0,37.07553,-5.879343)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2562"
+       xlink:href="#linearGradient6901"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient6925">
+      <stop
+         style="stop-color:#204a87;stop-opacity:1;"
+         offset="0"
+         id="stop6927" />
+      <stop
+         style="stop-color:#204a87;stop-opacity:0;"
+         offset="1"
+         id="stop6929" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient6901">
+      <stop
+         style="stop-color:#3465a4;stop-opacity:1;"
+         offset="0"
+         id="stop6903" />
+      <stop
+         style="stop-color:#3465a4;stop-opacity:0;"
+         offset="1"
+         id="stop6905" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2187"
+       inkscape:collect="always">
+      <stop
+         id="stop2189"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop2191"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4274">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0.25490198;"
+         offset="0.0000000"
+         id="stop4276" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4278" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient10691">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop10693" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop10695" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient15107">
+      <stop
+         id="stop15109"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15111"
+         offset="1.0000000"
+         style="stop-color:#e2e2e2;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient9749">
+      <stop
+         id="stop9751"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop9753"
+         offset="1.0000000"
+         style="stop-color:#ededed;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2274-5">
+      <stop
+         id="stop2276"
+         offset="0.0000000"
+         style="stop-color:#000000;stop-opacity:0.12871288;" />
+      <stop
+         id="stop2278"
+         offset="1.0000000"
+         style="stop-color:#000000;stop-opacity:0.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2624">
+      <stop
+         style="stop-color:#dfe0df;stop-opacity:1;"
+         offset="0"
+         id="stop2626" />
+      <stop
+         id="stop2630"
+         offset="0.23809524"
+         style="stop-color:#a6b0a6;stop-opacity:1;" />
+      <stop
+         style="stop-color:#b5beb5;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2628" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5464">
+      <stop
+         id="stop5466"
+         offset="0"
+         style="stop-color:#729fcf;stop-opacity:1;" />
+      <stop
+         id="stop5468"
+         offset="1"
+         style="stop-color:#afc9e4;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5476"
+       inkscape:collect="always">
+      <stop
+         id="stop5478"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop5480"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5048-5">
+      <stop
+         id="stop5050-4"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop5056-9" />
+      <stop
+         id="stop5052-2"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.177966,0,108.7434)"
+       r="29.036913"
+       fy="132.28575"
+       fx="61.518883"
+       cy="132.28575"
+       cx="61.518883"
+       id="radialGradient2801"
+       xlink:href="#linearGradient2795"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="76.313133"
+       x2="26.670298"
+       y1="76.176224"
+       x1="172.94208"
+       gradientTransform="matrix(0.562541,0,0,0.567972,-11.5974,-7.60954)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2258"
+       xlink:href="#linearGradient4689"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="144.75717"
+       x2="-65.308502"
+       y1="144.75717"
+       x1="224.23996"
+       gradientTransform="matrix(0.562541,0,0,0.567972,-11.5974,-7.60954)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2255"
+       xlink:href="#linearGradient4671"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="144.75717"
+       x2="-65.308502"
+       y1="144.75717"
+       x1="224.23996"
+       gradientTransform="translate(100.2702,99.61116)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2250"
+       xlink:href="#linearGradient4671"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="76.313133"
+       x2="26.670298"
+       y1="77.475983"
+       x1="172.94208"
+       gradientTransform="translate(100.2702,99.61116)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2248"
+       xlink:href="#linearGradient4689"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="144.75717"
+       x2="-65.308502"
+       y1="144.75717"
+       x1="224.23996"
+       gradientTransform="translate(100.2702,99.61116)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2589"
+       xlink:href="#linearGradient4671"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="76.313133"
+       x2="26.670298"
+       y1="77.475983"
+       x1="172.94208"
+       gradientTransform="translate(100.2702,99.61116)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2587"
+       xlink:href="#linearGradient4689"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientTransform="translate(100.2702,99.61116)"
+       gradientUnits="userSpaceOnUse"
+       xlink:href="#linearGradient4689"
+       id="linearGradient2990"
+       y2="76.313133"
+       x2="26.670298"
+       y1="77.475983"
+       x1="172.94208" />
+    <linearGradient
+       gradientTransform="translate(100.2702,99.61116)"
+       gradientUnits="userSpaceOnUse"
+       xlink:href="#linearGradient4671"
+       id="linearGradient2987"
+       y2="144.75717"
+       x2="-65.308502"
+       y1="144.75717"
+       x1="224.23996" />
+    <linearGradient
+       id="linearGradient4689">
+      <stop
+         id="stop4691"
+         offset="0"
+         style="stop-color:#5a9fd4;stop-opacity:1;" />
+      <stop
+         id="stop4693"
+         offset="1"
+         style="stop-color:#306998;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4671">
+      <stop
+         id="stop4673"
+         offset="0"
+         style="stop-color:#ffd43b;stop-opacity:1;" />
+      <stop
+         id="stop4675"
+         offset="1"
+         style="stop-color:#ffe873;stop-opacity:1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3236">
+      <stop
+         id="stop3244"
+         offset="0"
+         style="stop-color:#f4f4f4;stop-opacity:1" />
+      <stop
+         id="stop3240"
+         offset="1"
+         style="stop-color:white;stop-opacity:1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3676">
+      <stop
+         id="stop3678"
+         offset="0"
+         style="stop-color:#b2b2b2;stop-opacity:0.5;" />
+      <stop
+         id="stop3680"
+         offset="1"
+         style="stop-color:#b3b3b3;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2787">
+      <stop
+         id="stop2789"
+         offset="0"
+         style="stop-color:#7f7f7f;stop-opacity:0.5;" />
+      <stop
+         id="stop2791"
+         offset="1"
+         style="stop-color:#7f7f7f;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2795">
+      <stop
+         id="stop2797"
+         offset="0"
+         style="stop-color:#b8b8b8;stop-opacity:0.49803922;" />
+      <stop
+         id="stop2799"
+         offset="1"
+         style="stop-color:#7f7f7f;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4689"
+       id="linearGradient11109"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.562541,0,0,0.567972,-9.399749,-5.305317)"
+       x1="26.648937"
+       y1="20.603781"
+       x2="135.66525"
+       y2="114.39767" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4671"
+       id="linearGradient11111"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.562541,0,0,0.567972,-9.399749,-5.305317)"
+       x1="150.96111"
+       y1="192.35176"
+       x2="112.03144"
+       y2="137.27299" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2795"
+       id="radialGradient11113"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.382716e-8,-0.296405,1.43676,4.683673e-7,-128.544,150.5202)"
+       cx="61.518883"
+       cy="132.28575"
+       fx="61.518883"
+       fy="132.28575"
+       r="29.036913" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048"
+       id="linearGradient11177"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient11179"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060"
+       id="radialGradient11181"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259"
+       id="radialGradient11183"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="scale(0.960493,1.041132)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269"
+       id="radialGradient11185"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662"
+       id="radialGradient11187"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#aigrd2"
+       id="radialGradient11189"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.229703,0,0,0.229703,4.613529,3.979808)"
+       cx="20.892099"
+       cy="114.5684"
+       fx="20.892099"
+       fy="114.5684"
+       r="5.256" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#aigrd3"
+       id="radialGradient11191"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.229703,0,0,0.229703,4.613529,3.979808)"
+       cx="20.892099"
+       cy="64.567902"
+       fx="20.892099"
+       fy="64.567902"
+       r="5.257" />
+    <linearGradient
+       y2="31.026741"
+       x2="22.17771"
+       y1="33.357376"
+       x1="17.397203"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient1505"
+       xlink:href="#linearGradient2573-9"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="7.4121075"
+       x2="40.024059"
+       y1="4.2507305"
+       x1="11.841544"
+       gradientTransform="matrix(1.370928,0,0,-1.46456,2.525057,33.71269)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient1503"
+       xlink:href="#linearGradient9749-0"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="-7.5274644"
+       x2="17.178024"
+       y1="20.219761"
+       x1="10.027"
+       gradientTransform="matrix(1.570607,0,0,-1.231511,2.973436,33.33485)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient1501"
+       xlink:href="#linearGradient15107-6"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="11.981981"
+       x2="13.846983"
+       y1="11.48487"
+       x1="11.74217"
+       gradientTransform="matrix(1.296015,0,0,-1.43692,3.746576,33.20516)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient1497"
+       xlink:href="#linearGradient15107-6"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="11.981981"
+       x2="13.846983"
+       y1="11.48487"
+       x1="11.74217"
+       gradientTransform="matrix(1.276531,0,0,-1.406115,24.24763,33.3374)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient1493-0"
+       xlink:href="#linearGradient15107-6"
+       inkscape:collect="always" />
+    <radialGradient
+       r="17.977943"
+       fy="38.711506"
+       fx="27.741131"
+       cy="38.711506"
+       cx="27.741131"
+       gradientTransform="matrix(0.629929,0.459373,-0.147675,0.248512,16.51724,9.053737)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient1491"
+       xlink:href="#linearGradient2274-9"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="52.090679"
+       x2="9.8855038"
+       y1="38.070892"
+       x1="9.1643066"
+       gradientTransform="matrix(2.454781,0,0,0.762004,2.88175,0.337386)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient1487"
+       xlink:href="#linearGradient2624-6"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="26.022909"
+       x2="18.475286"
+       y1="4.7461624"
+       x1="11.572842"
+       gradientTransform="matrix(1.343475,0,0,1.505846,2.879511,-2.266018)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient1483"
+       xlink:href="#linearGradient15107-6"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4274-7">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0.25490198;"
+         offset="0.0000000"
+         id="stop4276-3" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop4278-5" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient15107-6">
+      <stop
+         id="stop15109-8"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop15111-9"
+         offset="1.0000000"
+         style="stop-color:#e2e2e2;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient9749-0">
+      <stop
+         id="stop9751-2"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop9753-3"
+         offset="1.0000000"
+         style="stop-color:#ededed;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2274-9">
+      <stop
+         id="stop2276-4"
+         offset="0.0000000"
+         style="stop-color:#000000;stop-opacity:0.12871288;" />
+      <stop
+         id="stop2278-2"
+         offset="1.0000000"
+         style="stop-color:#000000;stop-opacity:0.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2573-9"
+       inkscape:collect="always">
+      <stop
+         id="stop2575-9"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop2577-6"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2624-6">
+      <stop
+         style="stop-color:#dfe0df;stop-opacity:1;"
+         offset="0"
+         id="stop2626-1" />
+      <stop
+         id="stop2630-9"
+         offset="0.23809524"
+         style="stop-color:#a6b0a6;stop-opacity:1;" />
+      <stop
+         style="stop-color:#b5beb5;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2628-4" />
+    </linearGradient>
+    <linearGradient
+       y2="609.50507"
+       x2="302.85715"
+       y1="366.64789"
+       x1="302.85715"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient5027"
+       xlink:href="#linearGradient5048-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5048-2">
+      <stop
+         id="stop5050-7"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop5056-2" />
+      <stop
+         id="stop5052-01"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5029"
+       xlink:href="#linearGradient5060-4"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5060-4"
+       inkscape:collect="always">
+      <stop
+         id="stop5062-24"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop5064-0"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <radialGradient
+       r="117.14286"
+       fy="486.64789"
+       fx="605.71429"
+       cy="486.64789"
+       cx="605.71429"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient5031"
+       xlink:href="#linearGradient5060-4"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient5048-3">
+      <stop
+         id="stop5050-23"
+         offset="0"
+         style="stop-color:black;stop-opacity:0;" />
+      <stop
+         style="stop-color:black;stop-opacity:1;"
+         offset="0.5"
+         id="stop5056-8" />
+      <stop
+         id="stop5052-05"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5060-5"
+       inkscape:collect="always">
+      <stop
+         id="stop5062-1"
+         offset="0"
+         style="stop-color:black;stop-opacity:1;" />
+      <stop
+         id="stop5064-5"
+         offset="1"
+         style="stop-color:black;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient259-5">
+      <stop
+         style="stop-color:#fafafa;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop260-8" />
+      <stop
+         style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop261-9" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient269-3">
+      <stop
+         style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop270-6" />
+      <stop
+         style="stop-color:#4c4c4c;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop271-7" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient15662-6">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop15664-2" />
+      <stop
+         style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop15666-5" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       fy="114.5684"
+       fx="20.892099"
+       r="5.256"
+       cy="114.5684"
+       cx="20.892099"
+       id="aigrd2-0">
+      <stop
+         id="stop15566-0"
+         style="stop-color:#F0F0F0"
+         offset="0" />
+      <stop
+         id="stop15568-0"
+         style="stop-color:#9a9a9a;stop-opacity:1.0000000;"
+         offset="1.0000000" />
+    </radialGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       fy="64.567902"
+       fx="20.892099"
+       r="5.257"
+       cy="64.567902"
+       cx="20.892099"
+       id="aigrd3-2">
+      <stop
+         id="stop15573-8"
+         style="stop-color:#F0F0F0"
+         offset="0" />
+      <stop
+         id="stop15575-8"
+         style="stop-color:#9a9a9a;stop-opacity:1.0000000;"
+         offset="1.0000000" />
+    </radialGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5048-3"
+       id="linearGradient13544"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+       x1="302.85715"
+       y1="366.64789"
+       x2="302.85715"
+       y2="609.50507" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060-5"
+       id="radialGradient13546"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5060-5"
+       id="radialGradient13548"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+       cx="605.71429"
+       cy="486.64789"
+       fx="605.71429"
+       fy="486.64789"
+       r="117.14286" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient259-5"
+       id="radialGradient13550"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="scale(0.960493,1.041132)"
+       cx="33.966679"
+       cy="35.736916"
+       fx="33.966679"
+       fy="35.736916"
+       r="86.70845" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient269-3"
+       id="radialGradient13552"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       cx="8.824419"
+       cy="3.7561285"
+       fx="8.824419"
+       fy="3.7561285"
+       r="37.751713" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15662-6"
+       id="radialGradient13554"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.968273,0,0,1.032767,3.353553,0.646447)"
+       cx="8.1435566"
+       cy="7.2678967"
+       fx="8.1435566"
+       fy="7.2678967"
+       r="38.158695" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#aigrd2-0"
+       id="radialGradient13556"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.229703,0,0,0.229703,4.613529,3.979808)"
+       cx="20.892099"
+       cy="114.5684"
+       fx="20.892099"
+       fy="114.5684"
+       r="5.256" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#aigrd3-2"
+       id="radialGradient13558"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.229703,0,0,0.229703,4.613529,3.979808)"
+       cx="20.892099"
+       cy="64.567902"
+       fx="20.892099"
+       fy="64.567902"
+       r="5.257" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient13629"
+       id="linearGradient13635"
+       x1="135"
+       y1="317.36218"
+       x2="105"
+       y2="237.36218"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       y2="248.6311"
+       x2="153.0005"
+       y1="15.4238"
+       x1="99.777298"
+       gradientUnits="userSpaceOnUse"
+       id="aigrd1">
+      <stop
+         id="stop53300"
+         style="stop-color:#184375"
+         offset="0" />
+      <stop
+         id="stop53302"
+         style="stop-color:#C8BDDC"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2300">
+      <stop
+         style="stop-color:#000000;stop-opacity:0.32673267;"
+         offset="0.0000000"
+         id="stop2302" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop2304" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2300"
+       id="radialGradient15544"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.399258,-2.234445e-7,8.196178e-8,0.513264,4.365074,4.839285)"
+       cx="14.287618"
+       cy="68.872971"
+       fx="14.287618"
+       fy="72.568001"
+       r="11.68987" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#aigrd1"
+       id="linearGradient15546"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.200685,0,0,0.200685,-0.585758,-1.050787)"
+       x1="99.777298"
+       y1="15.4238"
+       x2="153.0005"
+       y2="248.6311" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15558"
+       id="linearGradient15564"
+       x1="350"
+       y1="352.36218"
+       x2="350"
+       y2="272.36218"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient15568"
+       id="linearGradient15574"
+       x1="350"
+       y1="452.36218"
+       x2="340"
+       y2="342.36218"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4"
+     inkscape:cx="249.31284"
+     inkscape:cy="117.69757"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="1280"
+     inkscape:window-height="750"
+     inkscape:window-x="-1"
+     inkscape:window-y="26"
+     showgrid="true"
+     inkscape:window-maximized="1"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0">
+    <inkscape:grid
+       type="xygrid"
+       id="grid10878"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true"
+       color="#0000ff"
+       opacity="0.04705882" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata1906">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Taso 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-60,-212.36218)">
+    <path
+       style="fill:url(#linearGradient15574);fill-opacity:1;stroke:none"
+       d="m 214,392.36218 25,-25 275,0 25,25 -25,25 -275,0 z"
+       id="path15566"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:url(#linearGradient15564);fill-opacity:1;stroke:none"
+       d="m 230,332.36218 0,-40 0,-10 290,0 0,50 -20,20 -90,0 0,5 -30,20 -30,-20 0,-5 -100,0 z"
+       id="path15548"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccccccc" />
+    <path
+       style="fill:url(#linearGradient13635);fill-opacity:1;stroke:none"
+       d="m 70,287.36218 0,-60 230,0 20,20 0,45 -35,20 -35,-20 0,-5 z"
+       id="path13627"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccc" />
+    <g
+       id="g10938"
+       transform="translate(0,0.21712644)">
+      <g
+         transform="matrix(0.69596564,0,0,0.69596564,116.34319,233.05094)"
+         id="g3694">
+        <g
+           id="layer6"
+           inkscape:label="Shadow">
+          <g
+             style="display:inline"
+             transform="matrix(0.02105461,0,0,0.02086758,42.85172,41.1536)"
+             id="g6707">
+            <rect
+               style="opacity:0.40206185;color:#000000;fill:url(#linearGradient11177);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+               id="rect6709"
+               width="1339.6335"
+               height="478.35718"
+               x="-1559.2523"
+               y="-150.69685" />
+            <path
+               inkscape:connector-curvature="0"
+               style="opacity:0.40206185;color:#000000;fill:url(#radialGradient11179);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+               d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z"
+               id="path6711"
+               sodipodi:nodetypes="cccc" />
+            <path
+               inkscape:connector-curvature="0"
+               sodipodi:nodetypes="cccc"
+               id="path6713"
+               d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z"
+               style="opacity:0.40206185;color:#000000;fill:url(#radialGradient11181);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
+          </g>
+        </g>
+        <g
+           style="display:inline"
+           inkscape:label="Base"
+           id="layer1-8">
+          <rect
+             style="color:#000000;fill:url(#radialGradient11183);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient11185);stroke-width:1.43685257;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:block;overflow:visible"
+             id="rect15391"
+             width="34.875"
+             height="40.920494"
+             x="6.6035528"
+             y="3.6464462"
+             ry="1.1490486" />
+          <rect
+             style="color:#000000;fill:none;stroke:url(#radialGradient11187);stroke-width:1.43685257;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:block;overflow:visible"
+             id="rect15660"
+             width="32.775887"
+             height="38.946384"
+             x="7.6660538"
+             y="4.5839462"
+             ry="0.14904857"
+             rx="0.14904857" />
+          <g
+             transform="translate(0.646447,-0.03798933)"
+             id="g2270">
+            <g
+               id="g1440"
+               style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.43685257;stroke-miterlimit:4"
+               transform="matrix(0.229703,0,0,0.229703,4.967081,4.244972)">
+              <radialGradient
+                 id="radialGradient1442"
+                 cx="20.892099"
+                 cy="114.5684"
+                 r="5.256"
+                 fx="20.892099"
+                 fy="114.5684"
+                 gradientUnits="userSpaceOnUse">
+                <stop
+                   offset="0"
+                   style="stop-color:#F0F0F0"
+                   id="stop1444" />
+                <stop
+                   offset="1"
+                   style="stop-color:#474747"
+                   id="stop1446" />
+              </radialGradient>
+              <path
+                 inkscape:connector-curvature="0"
+                 style="stroke:none"
+                 d="m 23.428,113.07 c 0,1.973 -1.6,3.572 -3.573,3.572 -1.974,0 -3.573,-1.6 -3.573,-3.572 0,-1.974 1.6,-3.573 3.573,-3.573 1.973,0 3.573,1.6 3.573,3.573 z"
+                 id="path1448" />
+              <radialGradient
+                 id="radialGradient1450"
+                 cx="20.892099"
+                 cy="64.567902"
+                 r="5.257"
+                 fx="20.892099"
+                 fy="64.567902"
+                 gradientUnits="userSpaceOnUse">
+                <stop
+                   offset="0"
+                   style="stop-color:#F0F0F0"
+                   id="stop1452" />
+                <stop
+                   offset="1"
+                   style="stop-color:#474747"
+                   id="stop1454" />
+              </radialGradient>
+              <path
+                 inkscape:connector-curvature="0"
+                 style="stroke:none"
+                 d="m 23.428,63.07 c 0,1.973 -1.6,3.573 -3.573,3.573 -1.974,0 -3.573,-1.6 -3.573,-3.573 0,-1.974 1.6,-3.573 3.573,-3.573 1.973,0 3.573,1.6 3.573,3.573 z"
+                 id="path1456" />
+            </g>
+            <path
+               inkscape:connector-curvature="0"
+               style="fill:url(#radialGradient11189);fill-rule:nonzero;stroke:none"
+               d="m 9.9950109,29.952326 c 0,0.453204 -0.3675248,0.820499 -0.8207288,0.820499 -0.4534338,0 -0.8207289,-0.367524 -0.8207289,-0.820499 0,-0.453434 0.3675248,-0.820729 0.8207289,-0.820729 0.453204,0 0.8207288,0.367525 0.8207288,0.820729 z"
+               id="path15570" />
+            <path
+               inkscape:connector-curvature="0"
+               style="fill:url(#radialGradient11191);fill-rule:nonzero;stroke:none"
+               d="m 9.9950109,18.467176 c 0,0.453204 -0.3675248,0.820729 -0.8207288,0.820729 -0.4534338,0 -0.8207289,-0.367525 -0.8207289,-0.820729 0,-0.453434 0.3675248,-0.820729 0.8207289,-0.820729 0.453204,0 0.8207288,0.367525 0.8207288,0.820729 z"
+               id="path15577" />
+          </g>
+          <path
+             inkscape:connector-curvature="0"
+             style="fill:none;stroke:#000000;stroke-width:1.42040503;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.01754384"
+             d="m 11.505723,5.4942766 0,37.9065924"
+             id="path15672"
+             sodipodi:nodetypes="cc" />
+          <path
+             inkscape:connector-curvature="0"
+             style="fill:none;stroke:#ffffff;stroke-width:1.43685257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.20467828"
+             d="m 12.5,5.0205154 0,38.0177126"
+             id="path15674"
+             sodipodi:nodetypes="cc" />
+        </g>
+        <g
+           id="layer5"
+           inkscape:label="Text"
+           style="display:inline">
+          <rect
+             ry="0.065390877"
+             rx="0.13778631"
+             y="9"
+             x="15.999994"
+             height="1"
+             width="20.000006"
+             id="rect15686"
+             style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+          <rect
+             ry="0.065390877"
+             rx="0.13778631"
+             y="11"
+             x="15.999994"
+             height="1"
+             width="20.000006"
+             id="rect15688"
+             style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+          <rect
+             ry="0.065390877"
+             rx="0.13778631"
+             y="13"
+             x="15.999994"
+             height="1"
+             width="20.000006"
+             id="rect15690"
+             style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+          <rect
+             ry="0.065390877"
+             rx="0.13778631"
+             y="15"
+             x="15.999994"
+             height="1"
+             width="20.000006"
+             id="rect15692"
+             style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+          <rect
+             ry="0.065390877"
+             rx="0.13778631"
+             y="17"
+             x="15.999994"
+             height="1"
+             width="20.000006"
+             id="rect15694"
+             style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+          <rect
+             ry="0.065390877"
+             rx="0.13778631"
+             y="19"
+             x="15.999994"
+             height="1"
+             width="20.000006"
+             id="rect15696"
+             style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+          <rect
+             ry="0.065390877"
+             rx="0.13778631"
+             y="21"
+             x="15.999994"
+             height="1"
+             width="20.000006"
+             id="rect15698"
+             style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+          <rect
+             ry="0.065390877"
+             rx="0.13778631"
+             y="23"
+             x="15.999994"
+             height="1"
+             width="20.000006"
+             id="rect15700"
+             style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+          <rect
+             ry="0.065390877"
+             rx="0.062003858"
+             y="25"
+             x="15.999986"
+             height="1"
+             width="9.0000057"
+             id="rect15732"
+             style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+          <g
+             id="g4849">
+            <rect
+               style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+               id="rect15736"
+               width="20.000006"
+               height="1"
+               x="15.999986"
+               y="29"
+               rx="0.13778631"
+               ry="0.065390877" />
+            <rect
+               style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+               id="rect15738"
+               width="20.000006"
+               height="1"
+               x="15.999986"
+               y="31"
+               rx="0.13778631"
+               ry="0.065390877" />
+            <rect
+               style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+               id="rect15740"
+               width="20.000006"
+               height="1"
+               x="15.999986"
+               y="33"
+               rx="0.13778631"
+               ry="0.065390877" />
+            <rect
+               style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+               id="rect15742"
+               width="20.000006"
+               height="1"
+               x="15.999986"
+               y="35"
+               rx="0.13778631"
+               ry="0.065390877" />
+            <rect
+               style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+               id="rect15744"
+               width="14.000014"
+               height="1"
+               x="15.999986"
+               y="37"
+               rx="0.096450485"
+               ry="0.065390877" />
+          </g>
+        </g>
+      </g>
+      <text
+         sodipodi:linespacing="125%"
+         id="text7676"
+         y="279.34702"
+         x="85.84211"
+         style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+         xml:space="preserve"><tspan
+           y="279.34702"
+           x="85.84211"
+           id="tspan7678"
+           sodipodi:role="line">MyMessage.proto</tspan></text>
+    </g>
+    <g
+       id="g11049"
+       transform="translate(74.824219,-16)">
+      <text
+         sodipodi:linespacing="125%"
+         id="text7692"
+         y="408.36218"
+         x="269.17578"
+         style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+         xml:space="preserve"><tspan
+           y="408.36218"
+           x="269.17578"
+           id="tspan7694"
+           sodipodi:role="line">pb_encode( );</tspan></text>
+      <text
+         sodipodi:linespacing="125%"
+         id="text7696"
+         y="421.36218"
+         x="269"
+         style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+         xml:space="preserve"><tspan
+           y="421.36218"
+           x="269"
+           id="tspan7698"
+           sodipodi:role="line">pb_decode( );</tspan></text>
+    </g>
+    <g
+       id="g10981"
+       transform="translate(40.612196,-41.994574)">
+      <text
+         sodipodi:linespacing="125%"
+         id="text7688"
+         y="321.55872"
+         x="161.02837"
+         style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+         xml:space="preserve"><tspan
+           y="321.55872"
+           x="161.02837"
+           id="tspan7690"
+           sodipodi:role="line">nanopb_generator.py</tspan></text>
+      <g
+         transform="matrix(0.20866613,0,0,0.20866613,166.72161,281.75965)"
+         id="g10620">
+        <path
+           inkscape:connector-curvature="0"
+           id="path46"
+           style="fill:#646464;fill-opacity:1"
+           d="m 184.61344,61.929363 c 0,-14.56215 -4.15226,-22.03817 -12.45678,-22.44755 -3.30427,-0.15595 -6.53055,0.37039 -9.66912,1.58878 -2.505,0.89673 -4.19124,1.78372 -5.07823,2.68045 l 0,34.75812 c 5.31216,3.33351 10.02976,4.88329 14.14303,4.63962 8.70415,-0.57508 13.0611,-7.64172 13.0611,-21.21942 z m 10.24419,0.60432 c 0,7.39804 -1.73498,13.53871 -5.22444,18.422 -3.88909,5.5266 -9.27923,8.37275 -16.17042,8.52871 -5.1952,0.1657 -10.54635,-1.46207 -16.05346,-4.87355 l 0,31.590317 -8.90884,-3.17755 0,-70.120567 c 1.46206,-1.79346 3.34325,-3.3335 5.62407,-4.63961 5.30242,-3.08983 11.74524,-4.67861 19.32848,-4.75658 l 0.12671,0.12671 c 6.93018,-0.08773 12.27159,2.75843 16.02422,8.5287 3.4992,5.29267 5.25368,12.07665 5.25368,20.37142 z" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path48"
+           style="fill:#646464;fill-opacity:1"
+           d="m 249.30487,83.265743 c 0,9.92254 -0.9942,16.794237 -2.9826,20.615097 -1.99816,3.82086 -5.79952,6.8717 -11.41385,9.14277 -4.55189,1.79346 -9.47417,2.76817 -14.75709,2.93387 l -1.47181,-5.61432 c 5.37064,-0.73103 9.15252,-1.46207 11.34561,-2.1931 4.31796,-1.46206 7.28108,-3.70389 8.90884,-6.706 1.30611,-2.446517 1.94942,-7.115367 1.94942,-14.026057 l 0,-2.3198 c -6.09193,2.76817 -12.47628,4.14251 -19.15303,4.14251 -4.38619,0 -8.25579,-1.37434 -11.58929,-4.14251 -3.74289,-3.01186 -5.61433,-6.83272 -5.61433,-11.46258 l 0,-37.07793 8.90884,-3.05084 0,37.3216 c 0,3.98656 1.28662,7.0569 3.85985,9.21101 2.57323,2.1541 5.90674,3.18729 9.99077,3.10932 4.08403,-0.08773 8.46047,-1.66676 13.10983,-4.75658 l 0,-43.54025 8.90884,0 0,48.41379 z" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path50"
+           style="fill:#646464;fill-opacity:1"
+           d="m 284.08249,88.997033 c -1.06243,0.08772 -2.03714,0.12671 -2.93387,0.12671 -5.03925,0 -8.96733,-1.19889 -11.77449,-3.60642 -2.79742,-2.40753 -4.20099,-5.73129 -4.20099,-9.97127 l 0,-35.08953 -6.10168,0 0,-5.60457 6.10168,0 0,-14.88381 8.89909,-3.16781 0,18.05162 10.01026,0 0,5.60457 -10.01026,0 0,34.84585 c 0,3.34325 0.89673,5.71179 2.6902,7.09588 1.54004,1.14041 3.98656,1.79347 7.32006,1.95917 l 0,4.63961 z" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path52"
+           style="fill:#646464;fill-opacity:1"
+           d="m 338.02288,88.266003 -8.90884,0 0,-34.38773 c 0,-3.49921 -0.81876,-6.51106 -2.44651,-9.02581 -1.88119,-2.84615 -4.49342,-4.26923 -7.84641,-4.26923 -4.08404,0 -9.19152,2.15411 -15.32242,6.46233 l 0,41.22044 -8.90885,0 0,-82.1972101 8.90885,-2.80716 0,37.4385701 c 5.6923,-4.14251 11.91093,-6.21864 18.66566,-6.21864 4.7176,0 8.53846,1.58877 11.46258,4.75658 2.93388,3.1678 4.39594,7.11537 4.39594,11.83296 l 0,37.1949 0,0 z" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path54"
+           style="fill:#646464;fill-opacity:1"
+           d="m 385.37424,60.525783 c 0,-5.59483 -1.06242,-10.21495 -3.17755,-13.87011 -2.51474,-4.45442 -6.42332,-6.80347 -11.70625,-7.04715 -9.76658,0.56534 -14.64012,7.56375 -14.64012,20.97574 0,6.15042 1.01369,11.28713 3.06057,15.41015 2.61223,5.25368 6.53056,7.84641 11.755,7.75869 9.80557,-0.07798 14.70835,-7.81717 14.70835,-23.22732 z m 9.75685,0.05848 c 0,7.96338 -2.03714,14.5914 -6.10168,19.88407 -4.47391,5.92623 -10.65357,8.89909 -18.53897,8.89909 -7.81716,0 -13.90909,-2.97286 -18.30503,-8.89909 -3.98656,-5.29267 -5.97497,-11.92069 -5.97497,-19.88407 0,-7.48576 2.15411,-13.78238 6.46232,-18.90935 4.5519,-5.43888 10.53661,-8.16806 17.93464,-8.16806 7.39805,0 13.42174,2.72918 18.06137,8.16806 4.3082,5.12697 6.46232,11.42359 6.46232,18.90935 z" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path56"
+           style="fill:#646464;fill-opacity:1"
+           d="m 446.20583,88.266003 -8.90884,0 0,-36.33715 c 0,-3.98656 -1.19889,-7.09588 -3.59667,-9.33772 -2.39779,-2.23208 -5.59483,-3.31401 -9.58139,-3.22628 -4.23023,0.07798 -8.25579,1.46206 -12.07664,4.14251 l 0,44.75864 -8.90884,0 0,-45.86006 c 5.12697,-3.73313 9.84456,-6.16991 14.15276,-7.31032 4.06455,-1.06243 7.65148,-1.58877 10.74131,-1.58877 2.11512,0 4.10352,0.20469 5.97496,0.61406 3.49921,0.80901 6.34535,2.31006 8.53845,4.51291 2.44651,2.43677 3.6649,5.3609 3.6649,8.78212 l 0,40.85006 z" />
+        <path
+           inkscape:connector-curvature="0"
+           style="fill:url(#linearGradient11109);fill-opacity:1"
+           d="m 60.510156,6.3979729 c -4.583653,0.021298 -8.960939,0.4122177 -12.8125,1.09375 C 36.35144,9.4962267 34.291407,13.691825 34.291406,21.429223 l 0,10.21875 26.8125,0 0,3.40625 -26.8125,0 -10.0625,0 c -7.792459,0 -14.6157592,4.683717 -16.7500002,13.59375 -2.46182,10.212966 -2.5710151,16.586023 0,27.25 1.9059283,7.937852 6.4575432,13.593748 14.2500002,13.59375 l 9.21875,0 0,-12.25 c 0,-8.849902 7.657144,-16.656248 16.75,-16.65625 l 26.78125,0 c 7.454951,0 13.406253,-6.138164 13.40625,-13.625 l 0,-25.53125 c 0,-7.266339 -6.12998,-12.7247775 -13.40625,-13.9375001 -4.605987,-0.7667253 -9.385097,-1.1150483 -13.96875,-1.09375 z m -14.5,8.2187501 c 2.769547,0 5.03125,2.298646 5.03125,5.125 -2e-6,2.816336 -2.261703,5.09375 -5.03125,5.09375 -2.779476,-1e-6 -5.03125,-2.277415 -5.03125,-5.09375 -1e-6,-2.826353 2.251774,-5.125 5.03125,-5.125 z"
+           id="path1948" />
+        <path
+           inkscape:connector-curvature="0"
+           style="fill:url(#linearGradient11111);fill-opacity:1"
+           d="m 91.228906,35.054223 0,11.90625 c 0,9.230755 -7.825895,16.999999 -16.75,17 l -26.78125,0 c -7.335833,0 -13.406249,6.278483 -13.40625,13.625 l 0,25.531247 c 0,7.26634 6.318588,11.54032 13.40625,13.625 8.487331,2.49561 16.626237,2.94663 26.78125,0 6.750155,-1.95439 13.406253,-5.88761 13.40625,-13.625 l 0,-10.218747 -26.78125,0 0,-3.40625 26.78125,0 13.406254,0 c 7.79246,0 10.69625,-5.435408 13.40624,-13.59375 2.79933,-8.398886 2.68022,-16.475776 0,-27.25 -1.92578,-7.757441 -5.60387,-13.59375 -13.40624,-13.59375 l -10.062504,0 z m -15.0625,64.65625 c 2.779478,3e-6 5.03125,2.277417 5.03125,5.093747 -2e-6,2.82635 -2.251775,5.125 -5.03125,5.125 -2.76955,0 -5.03125,-2.29865 -5.03125,-5.125 2e-6,-2.81633 2.261697,-5.093747 5.03125,-5.093747 z"
+           id="path1950" />
+        <path
+           inkscape:connector-curvature="0"
+           d="m 463.5544,26.909383 1.56195,0 0,-9.79624 3.70013,0 0,-1.16766 -8.96221,0 0,1.16766 3.70013,0 0,9.79624 m 6.64702,0 1.33447,0 0,-8.94703 2.89641,8.945906 1.48569,0 3.01816,-8.915576 0,8.9167 1.45579,0 0,-10.9639 -1.92589,0 -3.29831,9.392857 -2.81297,-9.392857 -2.15335,0 0,10.9639"
+           style="font-size:15.16445827px;font-style:normal;font-weight:normal;line-height:125%;fill:#646464;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+           id="text3004"
+           sodipodi:nodetypes="ccccccccccccccccccccccc" />
+        <path
+           sodipodi:type="arc"
+           style="opacity:0.44382019;fill:url(#radialGradient11113);fill-opacity:1;fill-rule:nonzero;stroke:none"
+           id="path1894"
+           sodipodi:cx="61.518883"
+           sodipodi:cy="132.28575"
+           sodipodi:rx="48.948284"
+           sodipodi:ry="8.6066771"
+           d="m 110.46717,132.28575 c 0,4.75334 -21.914896,8.60668 -48.948287,8.60668 -27.033391,0 -48.948284,-3.85334 -48.948284,-8.60668 0,-4.75334 21.914893,-8.60668 48.948284,-8.60668 27.033391,0 48.948287,3.85334 48.948287,8.60668 z"
+           transform="matrix(0.73406,0,0,0.809524,16.24958,27.00935)" />
+      </g>
+    </g>
+    <g
+       transform="translate(221.34207,203.5191)"
+       id="g10632">
+      <g
+         id="g10678"
+         inkscape:label="Text"
+         style="display:inline" />
+    </g>
+    <g
+       id="g13491"
+       transform="translate(24.020135,-23.566724)">
+      <text
+         sodipodi:linespacing="125%"
+         id="text7680"
+         y="360.05713"
+         x="215.79094"
+         style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+         xml:space="preserve"><tspan
+           y="360.05713"
+           x="215.79094"
+           id="tspan7682"
+           sodipodi:role="line">MyMessage.pb.c</tspan></text>
+      <text
+         sodipodi:linespacing="125%"
+         id="text7684"
+         y="346.05713"
+         x="215.73695"
+         style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+         xml:space="preserve"><tspan
+           y="346.05713"
+           x="215.73695"
+           id="tspan7686"
+           sodipodi:role="line">MyMessage.pb.h</tspan></text>
+    </g>
+    <g
+       id="g13560"
+       transform="translate(1.7323942,-7.4432063)">
+      <g
+         transform="matrix(0.69596564,0,0,0.69596564,207.04204,157.31974)"
+         id="g10800-7">
+        <g
+           transform="translate(221.34207,203.5191)"
+           id="g10634-0"
+           inkscape:label="Shadow">
+          <g
+             style="display:inline"
+             transform="matrix(0.02105461,0,0,0.02086758,42.85172,41.1536)"
+             id="g10636-1">
+            <rect
+               style="opacity:0.40206185;color:#000000;fill:url(#linearGradient13544);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+               id="rect10638-5"
+               width="1339.6335"
+               height="478.35718"
+               x="-1559.2523"
+               y="-150.69685" />
+            <path
+               inkscape:connector-curvature="0"
+               style="opacity:0.40206185;color:#000000;fill:url(#radialGradient13546);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+               d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z"
+               id="path10640-0"
+               sodipodi:nodetypes="cccc" />
+            <path
+               inkscape:connector-curvature="0"
+               sodipodi:nodetypes="cccc"
+               id="path10642-8"
+               d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z"
+               style="opacity:0.40206185;color:#000000;fill:url(#radialGradient13548);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
+          </g>
+        </g>
+        <g
+           transform="translate(221.34207,203.5191)"
+           style="display:inline"
+           inkscape:label="Base"
+           id="g10644-0">
+          <rect
+             style="color:#000000;fill:url(#radialGradient13550);fill-opacity:1;fill-rule:nonzero;stroke:url(#radialGradient13552);stroke-width:1.43685257;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:block;overflow:visible"
+             id="rect10646-7"
+             width="34.875"
+             height="40.920494"
+             x="6.6035528"
+             y="3.6464462"
+             ry="1.1490486" />
+          <rect
+             style="color:#000000;fill:none;stroke:url(#radialGradient13554);stroke-width:1.43685257;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:block;overflow:visible"
+             id="rect10648-1"
+             width="32.775887"
+             height="38.946384"
+             x="7.6660538"
+             y="4.5839462"
+             ry="0.14904857"
+             rx="0.14904857" />
+          <g
+             transform="translate(0.646447,-0.03798933)"
+             id="g10650-5">
+            <g
+               id="g10652-1"
+               style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.43685257;stroke-miterlimit:4"
+               transform="matrix(0.229703,0,0,0.229703,4.967081,4.244972)">
+              <radialGradient
+                 id="radialGradient10654-3"
+                 cx="20.892099"
+                 cy="114.5684"
+                 r="5.256"
+                 fx="20.892099"
+                 fy="114.5684"
+                 gradientUnits="userSpaceOnUse">
+                <stop
+                   offset="0"
+                   style="stop-color:#F0F0F0"
+                   id="stop10656-3" />
+                <stop
+                   offset="1"
+                   style="stop-color:#474747"
+                   id="stop10658-9" />
+              </radialGradient>
+              <path
+                 inkscape:connector-curvature="0"
+                 style="stroke:none"
+                 d="m 23.428,113.07 c 0,1.973 -1.6,3.572 -3.573,3.572 -1.974,0 -3.573,-1.6 -3.573,-3.572 0,-1.974 1.6,-3.573 3.573,-3.573 1.973,0 3.573,1.6 3.573,3.573 z"
+                 id="path10660-4" />
+              <radialGradient
+                 id="radialGradient10662-8"
+                 cx="20.892099"
+                 cy="64.567902"
+                 r="5.257"
+                 fx="20.892099"
+                 fy="64.567902"
+                 gradientUnits="userSpaceOnUse">
+                <stop
+                   offset="0"
+                   style="stop-color:#F0F0F0"
+                   id="stop10664-2" />
+                <stop
+                   offset="1"
+                   style="stop-color:#474747"
+                   id="stop10666-3" />
+              </radialGradient>
+              <path
+                 inkscape:connector-curvature="0"
+                 style="stroke:none"
+                 d="m 23.428,63.07 c 0,1.973 -1.6,3.573 -3.573,3.573 -1.974,0 -3.573,-1.6 -3.573,-3.573 0,-1.974 1.6,-3.573 3.573,-3.573 1.973,0 3.573,1.6 3.573,3.573 z"
+                 id="path10668-6" />
+            </g>
+            <path
+               inkscape:connector-curvature="0"
+               style="fill:url(#radialGradient13556);fill-rule:nonzero;stroke:none"
+               d="m 9.9950109,29.952326 c 0,0.453204 -0.3675248,0.820499 -0.8207288,0.820499 -0.4534338,0 -0.8207289,-0.367524 -0.8207289,-0.820499 0,-0.453434 0.3675248,-0.820729 0.8207289,-0.820729 0.453204,0 0.8207288,0.367525 0.8207288,0.820729 z"
+               id="path10670-0" />
+            <path
+               inkscape:connector-curvature="0"
+               style="fill:url(#radialGradient13558);fill-rule:nonzero;stroke:none"
+               d="m 9.9950109,18.467176 c 0,0.453204 -0.3675248,0.820729 -0.8207288,0.820729 -0.4534338,0 -0.8207289,-0.367525 -0.8207289,-0.820729 0,-0.453434 0.3675248,-0.820729 0.8207289,-0.820729 0.453204,0 0.8207288,0.367525 0.8207288,0.820729 z"
+               id="path10672-9" />
+          </g>
+          <path
+             inkscape:connector-curvature="0"
+             style="fill:none;stroke:#000000;stroke-width:1.42040503;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.01754384"
+             d="m 11.505723,5.4942766 0,37.9065924"
+             id="path10674-0"
+             sodipodi:nodetypes="cc" />
+          <path
+             inkscape:connector-curvature="0"
+             style="fill:none;stroke:#ffffff;stroke-width:1.43685257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.20467828"
+             d="m 12.5,5.0205154 0,38.0177126"
+             id="path10676-7"
+             sodipodi:nodetypes="cc" />
+        </g>
+        <rect
+           transform="translate(221.34207,203.5191)"
+           ry="0.065390877"
+           rx="0.13778631"
+           y="9"
+           x="15.999994"
+           height="1"
+           width="20.000006"
+           id="rect10680-0"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           ry="0.065390877"
+           rx="0.010779859"
+           y="11"
+           x="15.999994"
+           height="1"
+           width="1.5647217"
+           id="rect10682-2"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           ry="0.065390877"
+           rx="0.071673371"
+           y="13"
+           x="18.272837"
+           height="1"
+           width="10.403558"
+           id="rect10684-0"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           ry="0.065390877"
+           rx="0.093421049"
+           y="15"
+           x="18.272837"
+           height="1"
+           width="13.560284"
+           id="rect10686-0"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           ry="0.065390877"
+           rx="0.074283093"
+           y="17"
+           x="18.272837"
+           height="1"
+           width="10.782365"
+           id="rect10688-0"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           ry="0.065390877"
+           rx="0.010779865"
+           y="19"
+           x="18.272837"
+           height="1"
+           width="1.5647227"
+           id="rect10690-4"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           ry="0.065390877"
+           rx="0.041226614"
+           y="21"
+           x="20.166874"
+           height="1"
+           width="5.984139"
+           id="rect10692-0"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           ry="0.065390877"
+           rx="0.031657632"
+           y="23"
+           x="18.146568"
+           height="1"
+           width="4.5951796"
+           id="rect10694-8"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           ry="0.065390877"
+           rx="0.064613581"
+           y="25"
+           x="20.040596"
+           height="1"
+           width="9.3788128"
+           id="rect10696-1"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+           id="rect10730-1"
+           width="1.5501302"
+           height="1"
+           x="18.166864"
+           y="27"
+           rx="0.010679333"
+           ry="0.065390877" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+           id="rect10732-9"
+           width="1.5647217"
+           height="1"
+           x="15.999994"
+           y="29"
+           rx="0.010779859"
+           ry="0.065390877" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+           id="rect10734-9"
+           width="9.2671347"
+           height="1"
+           x="15.999994"
+           y="33"
+           rx="0.063844196"
+           ry="0.065390877" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+           id="rect10736-3"
+           width="1.5647217"
+           height="1"
+           x="15.999994"
+           y="35"
+           rx="0.010779859"
+           ry="0.065390877" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+           id="rect10738-9"
+           width="8.7620602"
+           height="1"
+           x="18.272837"
+           y="37"
+           rx="0.060364578"
+           ry="0.065390877" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+           id="rect10740-9"
+           width="10.277287"
+           height="1"
+           x="18.272837"
+           y="39"
+           rx="0.070803463"
+           ry="0.065390877" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           style="color:#000000;fill:#555753;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+           id="rect10742-1"
+           width="1.5647227"
+           height="1"
+           x="19.43354"
+           y="23"
+           rx="0.010779865"
+           ry="0.065390877" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           ry="0.065390877"
+           rx="0.010779865"
+           y="16.928572"
+           x="18.272825"
+           height="1"
+           width="1.5647227"
+           id="rect10744-2"
+           style="color:#000000;fill:#555753;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           style="color:#000000;fill:#75507b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+           id="rect10746-6"
+           width="3.9754369"
+           height="1"
+           x="15.951397"
+           y="8.9821434"
+           rx="0.027388033"
+           ry="0.065390877" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           ry="0.065390877"
+           rx="0.021236859"
+           y="13.000001"
+           x="18.272825"
+           height="1"
+           width="3.0825799"
+           id="rect10748-4"
+           style="color:#000000;fill:#75507b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           style="color:#000000;fill:#75507b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+           id="rect10750-7"
+           width="3.0825799"
+           height="1"
+           x="18.36211"
+           y="14.964287"
+           rx="0.021236859"
+           ry="0.065390877" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           ry="0.065390877"
+           rx="0.014470569"
+           y="33"
+           x="15.86211"
+           height="1"
+           width="2.1004369"
+           id="rect10752-9"
+           style="color:#000000;fill:#75507b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible" />
+        <rect
+           transform="translate(221.34207,203.5191)"
+           style="color:#000000;fill:#75507b;fill-opacity:0.54970757;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible"
+           id="rect10754-3"
+           width="2.1004369"
+           height="1"
+           x="18.272825"
+           y="36.92857"
+           rx="0.014470569"
+           ry="0.065390877" />
+        <text
+           xml:space="preserve"
+           style="font-size:22.49356079px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#2e3436;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+           x="247.85315"
+           y="242.62157"
+           id="text10726-2"
+           sodipodi:linespacing="125%"><tspan
+             sodipodi:role="line"
+             id="tspan10728-8"
+             x="247.85315"
+             y="242.62157">c</tspan></text>
+      </g>
+      <text
+         xml:space="preserve"
+         style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+         x="339"
+         y="345.36218"
+         id="text13355"
+         sodipodi:linespacing="125%"><tspan
+           sodipodi:role="line"
+           id="tspan13357"
+           x="339"
+           y="345.36218">Nanopb library</tspan></text>
+    </g>
+    <g
+       id="g11071"
+       transform="translate(81.439632,-41.53157)">
+      <g
+         id="g12984">
+        <text
+           xml:space="preserve"
+           style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+           x="193"
+           y="442.36218"
+           id="text10864"
+           sodipodi:linespacing="125%"><tspan
+             sodipodi:role="line"
+             id="tspan10866"
+             x="193"
+             y="442.36218">Protocol Buffers</tspan><tspan
+             sodipodi:role="line"
+             x="193"
+             y="457.36218"
+             id="tspan10868"
+             dy="-5">messages</tspan></text>
+        <g
+           transform="matrix(0.66229159,0,0,0.66229159,176.71024,408.41713)"
+           inkscape:label="Layer 1"
+           id="layer1-3">
+          <g
+             style="display:inline"
+             id="g5022"
+             transform="matrix(0.02312904,0,0,0.01485743,45.62082,30.57952)">
+            <rect
+               y="-150.69685"
+               x="-1559.2523"
+               height="478.35718"
+               width="1339.6335"
+               id="rect4173"
+               style="opacity:0.40206185;color:#000000;fill:url(#linearGradient5027);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
+            <path
+               inkscape:connector-curvature="0"
+               sodipodi:nodetypes="cccc"
+               id="path5058"
+               d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z"
+               style="opacity:0.40206185;color:#000000;fill:url(#radialGradient5029);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
+            <path
+               inkscape:connector-curvature="0"
+               style="opacity:0.40206185;color:#000000;fill:url(#radialGradient5031);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+               d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z"
+               id="path5018"
+               sodipodi:nodetypes="cccc" />
+          </g>
+          <g
+             id="g2570"
+             transform="matrix(1.004727,0,0,1.006001,0.05456518,-9.119156)"
+             inkscape:r_cx="true"
+             inkscape:r_cy="true">
+            <path
+               inkscape:connector-curvature="0"
+               id="path12723"
+               d="m 6.34375,15.454879 0,25.987281 36.96875,0 L 43.25,15.554447 c -1.3e-5,-0.0057 3.74e-4,-0.02709 0,-0.03319 -7.31e-4,-0.0065 0.0011,-0.02633 0,-0.03319 -0.0014,-0.0072 -0.02946,-0.02558 -0.03125,-0.03319 l -36.875,0 z"
+               style="fill:url(#linearGradient1483);fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.50185335;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+               inkscape:r_cx="true"
+               inkscape:r_cy="true" />
+            <path
+               inkscape:connector-curvature="0"
+               sodipodi:nodetypes="ccccccc"
+               id="path1634"
+               d="M 20.490674,29.058712 7.09471,40.0307 l 13.908842,-9.604306 9.018158,0 12.419047,9.482193 -11.863425,-10.849875 -10.086658,0 z"
+               style="fill:url(#linearGradient1487);fill-opacity:1;fill-rule:evenodd;stroke:none"
+               inkscape:r_cx="true"
+               inkscape:r_cy="true" />
+            <path
+               inkscape:connector-curvature="0"
+               id="path15103"
+               d="m 7.34375,16.733862 c -0.0067,0.01334 0.00539,0.01964 0,0.03159 -0.00237,0.0056 -0.029214,0.02632 -0.03125,0.03159 -0.0017,0.0049 0.00137,0.02704 0,0.03159 -0.00103,0.0042 6.858e-4,0.02777 0,0.03159 l 0.03125,23.473418 34.9375,0 -0.0625,-23.347047 c -6.87e-4,-0.0037 10e-4,-0.02751 0,-0.03159 -0.01466,-0.04808 -0.04183,-0.132156 -0.09375,-0.221149 l -34.78125,0 z"
+               style="fill:none;stroke:#ffffff;stroke-width:1.50185323;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+               inkscape:r_cx="true"
+               inkscape:r_cy="true" />
+            <path
+               inkscape:connector-curvature="0"
+               id="path2563"
+               d="M 23.329298,32.996721 C 20.937189,32.550375 7.9003872,18.771125 6.5966059,16.372022 6.5816495,16.343448 6.5559705,16.288608 6.5446896,16.2636 l 34.5131134,0 c -0.277079,2.502804 -7.524227,16.505746 -9.561279,16.733121 -0.0082,4.68e-4 -0.02128,0 -0.02927,0 l -8.020859,0 c -0.03363,0 -0.07755,0.0074 -0.117094,0 z"
+               style="fill:url(#radialGradient1491);fill-opacity:1;fill-rule:evenodd;stroke:none"
+               inkscape:r_cx="true"
+               inkscape:r_cy="true" />
+            <path
+               inkscape:connector-curvature="0"
+               id="path1613"
+               d="M 20.77475,31.085394 C 18.407309,30.694257 7.945269,18.619435 7.1185841,16.517089 7.109327,16.49205 7.094677,16.443993 7.088438,16.422079 l 35.542207,0 c -0.823616,2.19322 -11.29845,14.464065 -13.445143,14.663315 -0.0085,4.09e-4 -0.02191,0 -0.03015,0 l -8.260021,0 c -0.03463,0 -0.08145,0.0065 -0.120584,0 z"
+               style="fill:url(#linearGradient1497);fill-opacity:1;fill-rule:evenodd;stroke:#989898;stroke-width:1.28649366;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+               inkscape:r_cx="true"
+               inkscape:r_cy="true" />
+            <path
+               inkscape:connector-curvature="0"
+               id="path18153"
+               d="M 20.625174,30.490479 C 18.519211,29.999928 7.7224803,17.987711 7.0314243,16.466377 c -0.00254,-0.006 0.00218,-0.02669 0,-0.03231 -0.00545,-0.01576 -0.029096,-0.0523 -0.03125,-0.06463 2.87e-5,-0.0033 -4.061e-4,-0.02938 0,-0.03231 0.00117,-0.0021 0.029695,0.0017 0.03125,0 l 0.09375,-0.09694 35.4687497,0 c -0.0027,0.02433 -0.02268,0.06687 -0.03125,0.09694 -0.0075,0.0236 -0.02057,0.07023 -0.03125,0.09694 -0.922098,2.180937 -11.507988,13.766449 -13.34375,14.056416 -0.01493,0.0016 -0.04885,0 -0.0625,0 l -8.375,0 c -0.03029,-0.0017 -0.08975,0.0082 -0.125,0 z"
+               style="fill:url(#linearGradient1501);fill-opacity:1;fill-rule:evenodd;stroke:none"
+               inkscape:r_cx="true"
+               inkscape:r_cy="true" />
+            <path
+               inkscape:connector-curvature="0"
+               id="path1616"
+               d="M 20.875174,30.051141 C 18.427215,29.501671 8.7040003,18.433898 7.5314243,16.451725 l 34.5937497,0 c -1.490188,2.333171 -11.046672,13.411791 -13.15625,13.599416 -0.0087,4.02e-4 -0.02278,0 -0.03125,0 l -7.90625,0 c -0.02639,0 -0.06488,0.0036 -0.09375,0 -0.01979,-0.0031 -0.04165,0.0047 -0.0625,0 z"
+               style="fill:none;stroke:url(#linearGradient1503);stroke-width:1.2864933;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+               inkscape:r_cx="true"
+               inkscape:r_cy="true" />
+            <path
+               inkscape:connector-curvature="0"
+               sodipodi:nodetypes="cccccc"
+               id="path1636"
+               d="m 20.959511,30.447113 -11.941499,8.270856 2.219433,0.0061 9.998125,-6.86894 8.821908,-1.422838 -9.097967,0.01482 z"
+               style="fill:url(#linearGradient1505);fill-opacity:1;fill-rule:evenodd;stroke:none"
+               inkscape:r_cx="true"
+               inkscape:r_cy="true" />
+          </g>
+        </g>
+      </g>
+    </g>
+    <g
+       id="g11055"
+       transform="translate(68.952442,-27.959635)">
+      <text
+         sodipodi:linespacing="125%"
+         id="text10870"
+         y="435.36218"
+         x="367.90341"
+         style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+         xml:space="preserve"><tspan
+           y="435.36218"
+           x="367.90341"
+           id="tspan10872"
+           sodipodi:role="line">Data structures</tspan></text>
+      <g
+         transform="matrix(0.69596564,0,0,0.69596564,273.85443,81.742553)"
+         id="g10906">
+        <path
+           style="fill:none;stroke:#2e3436;stroke-width:1.43685257px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="m 180,479.36218 0,-3 4,0 0,-3"
+           id="path10902"
+           inkscape:connector-curvature="0"
+           sodipodi:nodetypes="cccc" />
+        <path
+           style="fill:none;stroke:#2e3436;stroke-width:1.43685257px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="m 206,479.36218 0,-3 -4,0 0,-3"
+           id="path10904"
+           inkscape:connector-curvature="0"
+           sodipodi:nodetypes="cccc" />
+        <g
+           id="g10880">
+          <rect
+             ry="0.040280778"
+             rx="0.010749566"
+             y="462.36218"
+             x="180"
+             height="11"
+             width="26"
+             id="rect10874"
+             style="color:#000000;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:1.43685257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+          <rect
+             style="color:#000000;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:#bdd2e9;stroke-width:1.43685257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+             id="rect10876"
+             width="24"
+             height="9.0000305"
+             x="181"
+             y="463.36215"
+             rx="0.0099226767"
+             ry="0.032957111" />
+        </g>
+        <g
+           id="g10892"
+           transform="translate(8,-2)">
+          <rect
+             style="color:#000000;fill:#d3d7cf;fill-opacity:1;fill-rule:nonzero;stroke:#4e9a06;stroke-width:1.43685257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+             id="rect10886"
+             width="13"
+             height="10.999969"
+             x="166"
+             y="481.36221"
+             rx="0.0053747827"
+             ry="0.040280666" />
+          <rect
+             ry="0.032956999"
+             rx="0.004547894"
+             y="482.36218"
+             x="167"
+             height="9"
+             width="11"
+             id="rect10888"
+             style="color:#000000;fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:#caf2a4;stroke-width:1.43685257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+        </g>
+        <g
+           transform="translate(33,-2)"
+           id="g10896">
+          <rect
+             ry="0.040280666"
+             rx="0.0053747827"
+             y="481.36221"
+             x="166"
+             height="10.999969"
+             width="13"
+             id="rect10898"
+             style="color:#000000;fill:#d3d7cf;fill-opacity:1;fill-rule:nonzero;stroke:#4e9a06;stroke-width:1.43685257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+          <rect
+             style="color:#000000;fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:#caf2a4;stroke-width:1.43685257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+             id="rect10900"
+             width="11"
+             height="9"
+             x="167"
+             y="482.36218"
+             rx="0.004547894"
+             ry="0.032956999" />
+        </g>
+      </g>
+    </g>
+    <g
+       id="g15531"
+       transform="translate(-16,0.55679368)">
+      <g
+         transform="matrix(0.79106547,0,0,0.79106547,463.64438,289.26048)"
+         id="g15500">
+        <g
+           inkscape:label="shadow"
+           id="layer2">
+          <path
+             transform="matrix(1.18638,0,0,1.18638,-4.539687,-7.794678)"
+             d="m 44.285715,38.714287 c 0,5.43296 -8.922325,9.837245 -19.928572,9.837245 -11.006246,0 -19.9285713,-4.404285 -19.9285713,-9.837245 0,-5.432961 8.9223253,-9.837245 19.9285713,-9.837245 11.006247,0 19.928572,4.404284 19.928572,9.837245 z"
+             sodipodi:ry="9.837245"
+             sodipodi:rx="19.928572"
+             sodipodi:cy="38.714287"
+             sodipodi:cx="24.357143"
+             id="path1538"
+             style="color:#000000;fill:url(#radialGradient15544);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.50000042;marker:none;visibility:visible;display:inline;overflow:visible"
+             sodipodi:type="arc" />
+        </g>
+        <g
+           id="layer1-4"
+           inkscape:label="Layer 1">
+          <path
+             inkscape:connector-curvature="0"
+             style="fill:url(#linearGradient15546);fill-rule:nonzero;stroke:#3f4561;stroke-width:1.26411784;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+             d="M 24.285801,43.196358 4.3751874,23.285744 24.285801,3.3751291 44.196415,23.285744 24.285801,43.196358 l 0,0 z"
+             id="path53304" />
+          <path
+             inkscape:connector-curvature="0"
+             sodipodi:nodetypes="ccccccc"
+             style="opacity:0.72000002;fill:#ffffff;fill-rule:nonzero;stroke:none"
+             d="M 43.505062,23.285744 24.285801,4.0664819 5.0665401,23.285744 5.8476076,23.910676 24.45724,5.4825431 43.505256,23.285744 l -1.94e-4,0 z"
+             id="path53359" />
+          <path
+             inkscape:connector-curvature="0"
+             style="opacity:0.5;fill:#ffffff;fill-rule:nonzero;stroke:none"
+             d="m 8.9257729,27.145172 0.7384498,-1.024184 c 0.6367493,0.268492 1.3006183,0.485069 1.9861833,0.644885 l -0.0058,1.576858 c 0.427728,0.08834 0.86301,0.156136 1.304105,0.204371 l 0.481774,-1.501889 c 0.344041,0.02848 0.691764,0.04417 1.043361,0.04417 0.351209,0 0.699124,-0.0155 1.043166,-0.04417 l 0.481775,1.501889 c 0.441288,-0.04823 0.876376,-0.116036 1.304104,-0.204371 l -0.006,-1.577051 c 0.685758,-0.159623 1.349433,-0.3762 1.986182,-0.644692 l 0.92248,1.279502 c 0.402351,-0.182094 0.794241,-0.382591 1.174895,-0.600522 l -0.492817,-1.498016 c 0.59723,-0.36225 1.161723,-0.773319 1.687471,-1.227972 l 1.272141,0.931779 c 0.325638,-0.296581 0.637329,-0.608272 0.933716,-0.93391 l -0.931585,-1.271947 c 0.454847,-0.525748 0.865916,-1.090047 1.228166,-1.687665 l 1.498015,0.493011 C 26.79347,21.2244 26.994161,20.832316 27.175867,20.43016 l -1.279308,-0.922287 c 0.268492,-0.636749 0.485068,-1.300618 0.645079,-1.986376 l 1.576663,0.0058 c 0.08834,-0.427727 0.156137,-0.86301 0.204178,-1.304298 l -1.501695,-0.481774 c 0.02886,-0.343848 0.04417,-0.691764 0.04417,-1.043167 0,-0.351403 -0.01569,-0.699125 -0.04417,-1.043361 l 1.501695,-0.481774 C 28.274632,12.73184 28.206442,12.296751 28.118495,11.86883 l -1.577051,0.006 C 26.381627,11.189076 26.165051,10.525208 25.896753,9.8886539 L 27.176061,8.9663652 C 26.994354,8.5640139 26.79347,8.1721237 26.575926,7.7912754 L 25.077717,8.2842867 C 24.715466,7.6868623 24.304398,7.1225635 23.849744,6.5970095 L 24.78133,5.3248686 C 24.502958,5.0189892 24.210252,4.7268638 23.905922,4.4467488 L 5.0669275,23.285938 6.0738693,24.29288 6.3725811,24.074174 c 0.5257484,0.454653 1.0900465,0.865722 1.6874698,1.227972 l -0.2419526,0.735157 1.1080622,1.108062 -3.876e-4,-1.93e-4 z"
+             id="path53361" />
+          <path
+             inkscape:connector-curvature="0"
+             style="opacity:0.5;fill:#ffffff;fill-rule:nonzero;stroke:none"
+             d="m 28.448976,32.191116 c 0,-6.484682 4.233883,-11.979469 10.08724,-13.874023 l -2.226972,-2.227167 c -0.01685,0.007 -0.0339,0.01298 -0.05056,0.02015 L 36.077171,15.858244 34.665167,14.44624 c -0.463178,0.2189 -0.91667,0.45446 -1.359314,0.707648 l 0.694089,2.109193 c -0.841314,0.509669 -1.635748,1.08869 -2.375747,1.728732 l -1.79111,-1.311659 c -0.458721,0.41746 -0.897297,0.856036 -1.314564,1.314565 l 1.311465,1.790914 c -0.640041,0.740195 -1.218868,1.534628 -1.728731,2.375748 l -2.109387,-0.694089 c -0.306654,0.536403 -0.589093,1.088304 -0.844994,1.654732 l 1.801182,1.298293 c -0.377942,0.896329 -0.682852,1.831014 -0.907758,2.796501 l -2.219999,-0.0085 c -0.124172,0.602266 -0.219869,1.215188 -0.287476,1.836051 l 2.114423,0.678398 c -0.04029,0.484293 -0.06199,0.97401 -0.06199,1.46857 0,0.494753 0.0217,0.98447 0.06199,1.468763 l -2.114423,0.677816 c 0.06761,0.621251 0.163304,1.233979 0.28767,1.836245 l 2.219805,-0.0083 c 0.224906,0.965487 0.529816,1.900172 0.907758,2.796502 l -1.801182,1.298486 c 0.142382,0.31479 0.293869,0.624931 0.452136,0.930423 l 3.804023,-3.803636 c -0.61602,-1.614245 -0.95425,-3.365836 -0.95425,-5.196269 l 1.93e-4,-1.94e-4 z"
+             id="path53363" />
+          <path
+             inkscape:connector-curvature="0"
+             sodipodi:nodetypes="ccccccc"
+             style="opacity:0.35;fill:#000000;fill-rule:nonzero;stroke:none"
+             d="M 5.2050478,23.424252 24.285801,42.505005 43.505062,23.285744 42.789963,22.603525 24.310314,41.041677 5.2050478,23.424059 l 0,1.93e-4 z"
+             id="path53365" />
+        </g>
+      </g>
+      <text
+         sodipodi:linespacing="125%"
+         id="text13355-1"
+         y="337.36218"
+         x="440"
+         style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+         xml:space="preserve"><tspan
+           y="337.36218"
+           x="440"
+           id="tspan13357-7"
+           sodipodi:role="line">User application</tspan></text>
+    </g>
+  </g>
+</svg>
diff --git a/third_party/nanopb/docs/index.rst b/third_party/nanopb/docs/index.rst
new file mode 100644
index 0000000..afc7ee4
--- /dev/null
+++ b/third_party/nanopb/docs/index.rst
@@ -0,0 +1,127 @@
+=============================================
+Nanopb: Protocol Buffers with small code size
+=============================================
+
+.. include :: menu.rst
+
+Nanopb is an ANSI-C library for encoding and decoding messages in Google's `Protocol Buffers`__ format with minimal requirements for RAM and code space.
+It is primarily suitable for 32-bit microcontrollers.
+
+__ https://developers.google.com/protocol-buffers/docs/reference/overview
+
+Overall structure
+=================
+
+For the runtime program, you always need *pb.h* for type declarations.
+Depending on whether you want to encode, decode, or both, you also need *pb_encode.h/c* or *pb_decode.h/c*.
+
+The high-level encoding and decoding functions take an array of *pb_field_t* structures, which describes the fields of a message structure. Usually you want these autogenerated from a *.proto* file. The tool script *nanopb_generator.py* accomplishes this.
+
+.. image:: generator_flow.png
+
+So a typical project might include these files:
+
+1) Nanopb runtime library:
+    - pb.h
+    - pb_common.h and pb_common.c (always needed)
+    - pb_decode.h and pb_decode.c (needed for decoding messages)
+    - pb_encode.h and pb_encode.c (needed for encoding messages)
+2) Protocol description (you can have many):
+    - person.proto (just an example)
+    - person.pb.c (autogenerated, contains initializers for const arrays)
+    - person.pb.h (autogenerated, contains type declarations)
+
+Features and limitations
+========================
+
+**Features**
+
+#) Pure C runtime
+#) Small code size (2–10 kB depending on processor, plus any message definitions)
+#) Small ram usage (typically ~300 bytes, plus any message structs)
+#) Allows specifying maximum size for strings and arrays, so that they can be allocated statically.
+#) No malloc needed: everything can be allocated statically or on the stack. Optional malloc support available.
+#) You can use either encoder or decoder alone to cut the code size in half.
+#) Support for most protobuf features, including: all data types, nested submessages, default values, repeated and optional fields, oneofs, packed arrays, extension fields.
+#) Callback mechanism for handling messages larger than can fit in available RAM.
+#) Extensive set of tests.
+
+**Limitations**
+
+#) Some speed has been sacrificed for code size.
+#) Encoding is focused on writing to streams. For memory buffers only it could be made more efficient.
+#) The deprecated Protocol Buffers feature called "groups" is not supported.
+#) Fields in the generated structs are ordered by the tag number, instead of the natural ordering in .proto file.
+#) Unknown fields are not preserved when decoding and re-encoding a message.
+#) Reflection (runtime introspection) is not supported. E.g. you can't request a field by giving its name in a string.
+#) Numeric arrays are always encoded as packed, even if not marked as packed in .proto.
+#) Cyclic references between messages are supported only in callback and malloc mode.
+
+Getting started
+===============
+
+For starters, consider this simple message::
+
+ message Example {
+    required int32 value = 1;
+ }
+
+Save this in *message.proto* and compile it::
+
+    user@host:~$ protoc -omessage.pb message.proto
+    user@host:~$ python nanopb/generator/nanopb_generator.py message.pb
+
+You should now have in *message.pb.h*::
+
+ typedef struct {
+    int32_t value;
+ } Example;
+ 
+ extern const pb_field_t Example_fields[2];
+
+Now in your main program do this to encode a message::
+
+ Example mymessage = {42};
+ uint8_t buffer[10];
+ pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+ pb_encode(&stream, Example_fields, &mymessage);
+
+After that, buffer will contain the encoded message.
+The number of bytes in the message is stored in *stream.bytes_written*.
+You can feed the message to *protoc --decode=Example message.proto* to verify its validity.
+
+For a complete example of the simple case, see *example/simple.c*.
+For a more complex example with network interface, see the *example/network_server* subdirectory.
+
+Compiler requirements
+=====================
+Nanopb should compile with most ansi-C compatible compilers. It however
+requires a few header files to be available:
+
+#) *string.h*, with these functions: *strlen*, *memcpy*, *memset*
+#) *stdint.h*, for definitions of *int32_t* etc.
+#) *stddef.h*, for definition of *size_t*
+#) *stdbool.h*, for definition of *bool*
+
+If these header files do not come with your compiler, you can use the
+file *extra/pb_syshdr.h* instead. It contains an example of how to provide
+the dependencies. You may have to edit it a bit to suit your custom platform.
+
+To use the pb_syshdr.h, define *PB_SYSTEM_HEADER* as *"pb_syshdr.h"* (including the quotes).
+Similarly, you can provide a custom include file, which should provide all the dependencies
+listed above.
+
+Running the test cases
+======================
+Extensive unittests and test cases are included under the *tests* folder.
+
+To build the tests, you will need the `scons`__ build system. The tests should
+be runnable on most platforms. Windows and Linux builds are regularly tested.
+
+__ http://www.scons.org/
+
+In addition to the build system, you will also need a working Google Protocol
+Buffers *protoc* compiler, and the Python bindings for Protocol Buffers. On
+Debian-based systems, install the following packages: *protobuf-compiler*,
+*python-protobuf* and *libprotobuf-dev*.
+
diff --git a/third_party/nanopb/docs/logo/logo.png b/third_party/nanopb/docs/logo/logo.png
new file mode 100644
index 0000000..0d9534f
--- /dev/null
+++ b/third_party/nanopb/docs/logo/logo.png
Binary files differ
diff --git a/third_party/nanopb/docs/logo/logo.svg b/third_party/nanopb/docs/logo/logo.svg
new file mode 100644
index 0000000..91ab28b
--- /dev/null
+++ b/third_party/nanopb/docs/logo/logo.svg
@@ -0,0 +1,1470 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="48"
+   height="48"
+   id="svg1901"
+   sodipodi:version="0.32"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="logo.svg"
+   version="1.1"
+   inkscape:export-filename="/home/petteri/svn-jpa/nanopb/docs/logo.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="7.9195959"
+     inkscape:cx="22.374283"
+     inkscape:cy="25.474909"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="1280"
+     inkscape:window-height="753"
+     inkscape:window-x="0"
+     inkscape:window-y="26"
+     showgrid="false"
+     inkscape:window-maximized="1"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:snap-nodes="false"
+     inkscape:snap-global="false">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4748"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs1903">
+    <linearGradient
+       id="linearGradient4822">
+      <stop
+         id="stop4824"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         id="stop4826"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4810">
+      <stop
+         style="stop-color:#747474;stop-opacity:0.40000001"
+         offset="0"
+         id="stop4812" />
+      <stop
+         style="stop-color:#747474;stop-opacity:0;"
+         offset="1"
+         id="stop4814" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4768">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4770" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop4772" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4758">
+      <stop
+         style="stop-color:#2c2c2c;stop-opacity:1;"
+         offset="0"
+         id="stop4760" />
+      <stop
+         id="stop4762"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4342">
+      <stop
+         style="stop-color:#0c0c0c;stop-opacity:1"
+         offset="0"
+         id="stop4344" />
+      <stop
+         style="stop-color:#212121;stop-opacity:0;"
+         offset="1"
+         id="stop4346" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4294">
+      <stop
+         style="stop-color:#ababab;stop-opacity:1;"
+         offset="0"
+         id="stop4296" />
+      <stop
+         id="stop4298"
+         offset="1"
+         style="stop-color:#454545;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4250">
+      <stop
+         style="stop-color:#4b4b4b;stop-opacity:1;"
+         offset="0"
+         id="stop4252" />
+      <stop
+         style="stop-color:#353535;stop-opacity:1;"
+         offset="1"
+         id="stop4254" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4206">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop4208" />
+      <stop
+         style="stop-color:#525252;stop-opacity:1;"
+         offset="1"
+         id="stop4210" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4196">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop4198" />
+      <stop
+         id="stop4200"
+         offset="1"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4128">
+      <stop
+         style="stop-color:#101010;stop-opacity:1;"
+         offset="0"
+         id="stop4130" />
+      <stop
+         style="stop-color:#101010;stop-opacity:0;"
+         offset="1"
+         id="stop4132" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4098">
+      <stop
+         id="stop4106"
+         offset="0"
+         style="stop-color:#393939;stop-opacity:1;" />
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="1"
+         id="stop4102" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3927">
+      <stop
+         style="stop-color:#424242;stop-opacity:0;"
+         offset="0"
+         id="stop3929" />
+      <stop
+         style="stop-color:#424242;stop-opacity:1;"
+         offset="1"
+         id="stop3931" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3855">
+      <stop
+         style="stop-color:#666666;stop-opacity:1;"
+         offset="0"
+         id="stop3857" />
+      <stop
+         style="stop-color:#666666;stop-opacity:1;"
+         offset="1"
+         id="stop3859" />
+    </linearGradient>
+    <filter
+       inkscape:collect="always"
+       id="filter3937"
+       x="-0.12812556"
+       width="1.2562511"
+       y="-0.16742191"
+       height="1.3348438"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="6.9443171"
+         id="feGaussianBlur3939" />
+    </filter>
+    <mask
+       maskUnits="userSpaceOnUse"
+       id="mask3953">
+      <path
+         sodipodi:type="arc"
+         style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         id="path3955"
+         sodipodi:cx="352.14285"
+         sodipodi:cy="623.07648"
+         sodipodi:rx="40.714287"
+         sodipodi:ry="40.714287"
+         d="m 392.85714,623.07648 a 40.714287,40.714287 0 1 1 -81.42857,0 40.714287,40.714287 0 1 1 81.42857,0 z"
+         transform="matrix(1.7543859,0,0,1.7543859,-339.22305,-467.89728)" />
+    </mask>
+    <filter
+       inkscape:collect="always"
+       id="filter3979"
+       x="-0.3823055"
+       width="1.764611"
+       y="-0.47600195"
+       height="1.952004"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="9.0109491"
+         id="feGaussianBlur3981" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter4049"
+       x="-0.16508646"
+       width="1.3301729"
+       y="-0.2055463"
+       height="1.4110926"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="3.8910917"
+         id="feGaussianBlur4051" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter4077"
+       x="-0.10677131"
+       width="1.2135426"
+       y="-0.13951825"
+       height="1.2790365"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="5.7869309"
+         id="feGaussianBlur4079" />
+    </filter>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4098"
+       id="radialGradient4108"
+       cx="141.30658"
+       cy="537.61609"
+       fx="141.30658"
+       fy="537.61609"
+       r="62.941189"
+       gradientTransform="matrix(0.25806011,0.29422394,-0.03785018,0.03319792,-54.888524,963.70096)"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4098"
+       id="radialGradient4116"
+       cx="241.51366"
+       cy="562.11151"
+       fx="241.51366"
+       fy="562.11151"
+       r="61.588345"
+       gradientTransform="matrix(0.23965697,0.20183681,-0.0408911,0.04855325,-57.403613,954.12796)"
+       gradientUnits="userSpaceOnUse" />
+    <filter
+       inkscape:collect="always"
+       id="filter4124"
+       x="-0.13548391"
+       width="1.2709678"
+       y="-0.13548385"
+       height="1.2709677"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.5681804"
+         id="feGaussianBlur4126" />
+    </filter>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4128"
+       id="linearGradient4134"
+       x1="223.01562"
+       y1="596.02582"
+       x2="249.23067"
+       y2="615.72375"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4196"
+       id="radialGradient4166"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21059566,0.18185717,-0.04148798,0.04804422,-52.42241,1007.9653)"
+       cx="241.51366"
+       cy="562.11151"
+       fx="241.51366"
+       fy="562.11151"
+       r="61.588345" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4128"
+       id="linearGradient4168"
+       gradientUnits="userSpaceOnUse"
+       x1="223.01562"
+       y1="596.02582"
+       x2="244.1799"
+       y2="609.66284"
+       gradientTransform="translate(-15.152288,311.12698)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4196"
+       id="radialGradient4170"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21753099,0.27226679,-0.05516349,0.04407357,-42.593175,1009.5278)"
+       cx="133.18637"
+       cy="535.45135"
+       fx="133.18637"
+       fy="535.45135"
+       r="62.941189" />
+    <filter
+       inkscape:collect="always"
+       id="filter4172"
+       x="-0.19549713"
+       width="1.3909943"
+       y="-0.2434101"
+       height="1.4868202"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="4.6078717"
+         id="feGaussianBlur4174" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter4192"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.2830565"
+         id="feGaussianBlur4194" />
+    </filter>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4128"
+       id="linearGradient4204"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-125.50943,203.26983)"
+       x1="223.01562"
+       y1="596.02582"
+       x2="247.3942"
+       y2="621.80566" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4294"
+       id="radialGradient4288"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23965697,0.20183681,-0.0408911,0.04855325,-56.508695,902.22267)"
+       cx="241.51366"
+       cy="562.11151"
+       fx="241.51366"
+       fy="562.11151"
+       r="61.588345" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4128"
+       id="linearGradient4290"
+       gradientUnits="userSpaceOnUse"
+       x1="223.01562"
+       y1="596.02582"
+       x2="249.23067"
+       y2="615.72375"
+       gradientTransform="translate(5.7142857,-331.42857)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4294"
+       id="radialGradient4292"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.25806011,0.29422394,-0.03785018,0.03319792,-53.993604,911.79568)"
+       cx="141.30658"
+       cy="537.61609"
+       fx="141.30658"
+       fy="537.61609"
+       r="62.941189" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4128-1"
+       id="linearGradient4204-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-125.50943,203.26983)"
+       x1="223.01562"
+       y1="596.02582"
+       x2="247.3942"
+       y2="621.80566" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4128-1">
+      <stop
+         style="stop-color:#101010;stop-opacity:1;"
+         offset="0"
+         id="stop4130-9" />
+      <stop
+         style="stop-color:#101010;stop-opacity:0;"
+         offset="1"
+         id="stop4132-6" />
+    </linearGradient>
+    <filter
+       color-interpolation-filters="sRGB"
+       inkscape:collect="always"
+       id="filter4192-5">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.2830565"
+         id="feGaussianBlur4194-3" />
+    </filter>
+    <linearGradient
+       y2="623.88721"
+       x2="257.00116"
+       y1="588.95477"
+       x1="214.42932"
+       gradientTransform="translate(-103.05154,-435.68082)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4317"
+       xlink:href="#linearGradient4128-1"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4342"
+       id="linearGradient4348"
+       x1="199.86082"
+       y1="194.70784"
+       x2="205.35472"
+       y2="200.26366"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.15661078,0,0,0.15661078,-60.981634,940.60962)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4758"
+       id="radialGradient4380"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.22197967,0.2098482,-0.03289985,0.03480181,-3.6600532,960.31534)"
+       cx="233.06406"
+       cy="558.55359"
+       fx="233.06406"
+       fy="558.55359"
+       r="61.588345" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4128"
+       id="linearGradient4382"
+       gradientUnits="userSpaceOnUse"
+       x1="223.01562"
+       y1="596.02582"
+       x2="244.66977"
+       y2="621.13983"
+       gradientTransform="translate(353.55339,-2.0203051)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4098"
+       id="radialGradient4384"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.26804686,0.27059786,-0.03479311,0.03446511,-3.4683138,966.86956)"
+       cx="141.30658"
+       cy="537.61609"
+       fx="141.30658"
+       fy="537.61609"
+       r="62.941189" />
+    <linearGradient
+       y2="623.88721"
+       x2="257.00116"
+       y1="588.95477"
+       x1="214.42932"
+       gradientTransform="translate(-103.05154,-435.68082)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4317-7"
+       xlink:href="#linearGradient4128-1-3"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4128-1-3">
+      <stop
+         style="stop-color:#101010;stop-opacity:1;"
+         offset="0"
+         id="stop4130-9-1" />
+      <stop
+         style="stop-color:#101010;stop-opacity:0;"
+         offset="1"
+         id="stop4132-6-8" />
+    </linearGradient>
+    <filter
+       color-interpolation-filters="sRGB"
+       inkscape:collect="always"
+       id="filter4192-5-8">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.2830565"
+         id="feGaussianBlur4194-3-2" />
+    </filter>
+    <linearGradient
+       y2="623.88721"
+       x2="257.00116"
+       y1="588.95477"
+       x1="214.42932"
+       gradientTransform="translate(243.90853,-108.62729)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4403"
+       xlink:href="#linearGradient4128-1-3"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4342-4"
+       id="linearGradient4348-2"
+       x1="199.86082"
+       y1="194.70784"
+       x2="205.35472"
+       y2="200.26366"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4342-4">
+      <stop
+         style="stop-color:#0c0c0c;stop-opacity:1"
+         offset="0"
+         id="stop4344-9" />
+      <stop
+         style="stop-color:#212121;stop-opacity:0;"
+         offset="1"
+         id="stop4346-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.15234083,0,0,0.15234083,-7.1415876,993.54015)"
+       y2="213.52541"
+       x2="223.58961"
+       y1="207.96959"
+       x1="218.92458"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4441"
+       xlink:href="#linearGradient4342-4"
+       inkscape:collect="always" />
+    <filter
+       inkscape:collect="always"
+       id="filter4480"
+       x="-0.18364665"
+       width="1.3672934"
+       y="-0.23997138"
+       height="1.4799428"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="9.9535211"
+         id="feGaussianBlur4482" />
+    </filter>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath4507">
+      <path
+         transform="matrix(1.7543859,0,0,1.7543859,-354.37534,-156.7703)"
+         d="m 392.85714,623.07648 a 40.714287,40.714287 0 1 1 -81.42857,0 40.714287,40.714287 0 1 1 81.42857,0 z"
+         sodipodi:ry="40.714287"
+         sodipodi:rx="40.714287"
+         sodipodi:cy="623.07648"
+         sodipodi:cx="352.14285"
+         id="path4509"
+         style="color:#000000;fill:#ededed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         sodipodi:type="arc" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath4507-9">
+      <path
+         transform="matrix(1.7543859,0,0,1.7543859,-354.37534,-156.7703)"
+         d="m 392.85714,623.07648 a 40.714287,40.714287 0 1 1 -81.42857,0 40.714287,40.714287 0 1 1 81.42857,0 z"
+         sodipodi:ry="40.714287"
+         sodipodi:rx="40.714287"
+         sodipodi:cy="623.07648"
+         sodipodi:cx="352.14285"
+         id="path4509-8"
+         style="color:#000000;fill:#ededed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         sodipodi:type="arc" />
+    </clipPath>
+    <filter
+       color-interpolation-filters="sRGB"
+       inkscape:collect="always"
+       id="filter4501-0">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="3.929404"
+         id="feGaussianBlur4503-0" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter4547"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="6.0452369"
+         id="feGaussianBlur4549" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter4571"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.1115556"
+         id="feGaussianBlur4573" />
+    </filter>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath4507-9-7">
+      <path
+         transform="matrix(1.7543859,0,0,1.7543859,-354.37534,-156.7703)"
+         d="m 392.85714,623.07648 a 40.714287,40.714287 0 1 1 -81.42857,0 40.714287,40.714287 0 1 1 81.42857,0 z"
+         sodipodi:ry="40.714287"
+         sodipodi:rx="40.714287"
+         sodipodi:cy="623.07648"
+         sodipodi:cx="352.14285"
+         id="path4509-8-1"
+         style="color:#000000;fill:#ededed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         sodipodi:type="arc" />
+    </clipPath>
+    <filter
+       inkscape:collect="always"
+       id="filter4625"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="4.5339277"
+         id="feGaussianBlur4627" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter4641"
+       x="-0.3823055"
+       width="1.764611"
+       y="-0.47600195"
+       height="1.952004"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="9.0109492"
+         id="feGaussianBlur4643" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter4649"
+       x="-0.57345825"
+       width="2.1469164"
+       y="-0.71400291"
+       height="2.4280059"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="13.516424"
+         id="feGaussianBlur4651" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter4661"
+       x="-0.23191024"
+       width="1.4638205"
+       y="-0.23260693"
+       height="1.4652139"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="12.425148"
+         id="feGaussianBlur4663" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter4738"
+       color-interpolation-filters="sRGB">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.13338853"
+         id="feGaussianBlur4740" />
+    </filter>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath4744">
+      <path
+         transform="matrix(0.08521814,0,0,0.08591999,4.0670251,-20.374151)"
+         d="m 392.85714,623.07648 a 40.714287,40.714287 0 1 1 -81.42857,0 40.714287,40.714287 0 1 1 81.42857,0 z"
+         sodipodi:ry="40.714287"
+         sodipodi:rx="40.714287"
+         sodipodi:cy="623.07648"
+         sodipodi:cx="352.14285"
+         id="path4746"
+         style="color:#000000;fill:#2c2c2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         sodipodi:type="arc" />
+    </clipPath>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4128"
+       id="linearGradient4750"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-15.152288,311.12698)"
+       x1="223.01562"
+       y1="596.02582"
+       x2="244.1799"
+       y2="609.66284" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4768"
+       id="linearGradient4774"
+       x1="-8.5135946"
+       y1="1040.8162"
+       x2="-36.138241"
+       y2="1040.5483"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.61534323,0,0,0.61534326,14.59514,0.04606772)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4768"
+       id="linearGradient4776"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.61534323,0,0,0.61534326,14.59514,0.04606772)"
+       x1="-22.697203"
+       y1="1040.3197"
+       x2="-39.949551"
+       y2="1039.9578" />
+    <filter
+       inkscape:collect="always"
+       id="filter4794"
+       x="-0.029811953"
+       width="1.0596239"
+       y="-0.1456968"
+       height="1.2913936">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.18351365"
+         id="feGaussianBlur4796" />
+    </filter>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath4804">
+      <path
+         transform="matrix(0.08147906,-0.06254913,0.06129679,0.08116836,-38.523905,-0.095885)"
+         sodipodi:type="arc"
+         style="color:#000000;fill:#2e2e2e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.45699024;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         id="path4806"
+         sodipodi:cx="352.14285"
+         sodipodi:cy="623.07648"
+         sodipodi:rx="40.714287"
+         sodipodi:ry="40.714287"
+         d="m 392.85714,623.07648 a 40.714287,40.714287 0 1 1 -81.42857,0 40.714287,40.714287 0 1 1 81.42857,0 z" />
+    </clipPath>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4810"
+       id="radialGradient4816"
+       cx="10.46172"
+       cy="1017.2507"
+       fx="10.46172"
+       fy="1017.2507"
+       r="9.5885149"
+       gradientTransform="matrix(0.19619751,-0.12064592,0.46999446,0.76431711,-469.23407,241.69554)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4822"
+       id="linearGradient4820"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(353.55339,-2.0203051)"
+       x1="215.9617"
+       y1="586.76971"
+       x2="253.67445"
+       y2="578.57703" />
+    <filter
+       inkscape:collect="always"
+       id="filter4914"
+       x="-0.061783516"
+       width="1.123567"
+       y="-0.10365072"
+       height="1.2073014">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.77641645"
+         id="feGaussianBlur4916" />
+    </filter>
+  </defs>
+  <metadata
+     id="metadata1906">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:groupmode="layer"
+     inkscape:label="Taso 1"
+     transform="translate(0,-1004.3622)">
+    <text
+       sodipodi:linespacing="125%"
+       id="text3796"
+       y="609.03595"
+       x="-131.45427"
+       style="font-size:41.48686981px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+       xml:space="preserve"><tspan
+         y="609.03595"
+         x="-131.45427"
+         id="tspan3798"
+         sodipodi:role="line" /></text>
+    <path
+       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4547);enable-background:accumulate"
+       d="m 263.40625,861.375 c -41.40153,0 -74.9375,33.56722 -74.9375,74.96875 0,41.40153 33.53597,74.96875 74.9375,74.96875 41.40153,0 74.96875,-33.56722 74.96875,-74.96875 0,-41.40153 -33.56722,-74.96875 -74.96875,-74.96875 z m 0,3.53125 c 39.44891,0 71.4375,31.98859 71.4375,71.4375 0,39.44891 -31.98859,71.43745 -71.4375,71.43745 -39.44891,0 -71.40625,-31.98854 -71.40625,-71.43745 0,-39.44891 31.95734,-71.4375 71.40625,-71.4375 z"
+       id="path4494"
+       clip-path="url(#clipPath4507)"
+       inkscape:connector-curvature="0"
+       transform="matrix(0.15234083,0,0,0.15234083,-3.588174,896.00387)"
+       inkscape:export-filename="/home/petteri/svn-jpa/nanopb/docs/text4764.png"
+       inkscape:export-xdpi="66.074806"
+       inkscape:export-ydpi="66.074806" />
+    <path
+       sodipodi:nodetypes="sssss"
+       inkscape:connector-curvature="0"
+       id="path4350"
+       d="m 19.002637,1019.1304 c 2.257937,0.8279 11.784525,9.0752 16.335834,13.3706 1.356388,1.2801 -0.01672,2.5743 -1.251365,1.5234 -4.260991,-3.6267 -10.985076,-9.7625 -16.172619,-13.561 -1.673691,-1.2256 -0.341266,-1.8571 1.08815,-1.333 z"
+       style="color:#000000;fill:url(#radialGradient4380);fill-opacity:1.0;stroke:none;stroke-width:0.35433071999999999;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <path
+       style="opacity:0.35655738999999997;color:#000000;fill:url(#linearGradient4441);fill-opacity:1;stroke:none;stroke-width:0.35433071999999999;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="m 18.632723,1018.8098 c 2.257926,0.8278 11.784523,9.0751 16.335833,13.3706 1.356387,1.2801 -0.01672,2.5742 -1.251366,1.5234 -4.260991,-3.6269 -10.985075,-9.7625 -16.172619,-13.5611 -1.673691,-1.2256 -0.341265,-1.8571 1.088152,-1.3329 z"
+       id="path4340-7"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="sssss" />
+    <path
+       d="m 392.85714,623.07648 a 40.714287,40.714287 0 1 1 -81.42857,0 40.714287,40.714287 0 1 1 81.42857,0 z"
+       sodipodi:ry="40.714287"
+       sodipodi:rx="40.714287"
+       sodipodi:cy="623.07648"
+       sodipodi:cx="352.14285"
+       id="path4352"
+       style="color:#000000;fill:#2e2e2e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.45699024;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       sodipodi:type="arc"
+       transform="matrix(0.21247535,0,0,0.21247535,-61.734636,883.97609)" />
+    <path
+       sodipodi:type="arc"
+       style="color:#000000;fill:#2c2c2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="path4354"
+       sodipodi:cx="352.14285"
+       sodipodi:cy="623.07648"
+       sodipodi:rx="40.714287"
+       sodipodi:ry="40.714287"
+       d="m 392.85714,623.07648 a 40.714287,40.714287 0 1 1 -81.42857,0 40.714287,40.714287 0 1 1 81.42857,0 z"
+       transform="matrix(0.2672646,0,0,0.2672646,-57.578671,872.09085)"
+       inkscape:export-filename="/home/petteri/svn-jpa/nanopb/docs/text4764.png"
+       inkscape:export-xdpi="66.074806"
+       inkscape:export-ydpi="66.074806" />
+    <g
+       id="g4752"
+       transform="matrix(0.99202587,0,0,0.92871672,0.27451699,74.105593)"
+       inkscape:export-filename="/home/petteri/svn-jpa/nanopb/docs/text4764.png"
+       inkscape:export-xdpi="66.074806"
+       inkscape:export-ydpi="66.074806">
+      <text
+         transform="matrix(0.15234083,0,0,0.15234083,-59.761767,943.67847)"
+         sodipodi:linespacing="125%"
+         id="text4551"
+         y="646.5647"
+         x="593.79944"
+         style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter4571);font-family:FreeSerif;-inkscape-font-specification:FreeSerif"
+         xml:space="preserve"><tspan
+           y="646.5647"
+           x="593.79944"
+           id="tspan4553"
+           sodipodi:role="line">Pb</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:10.96853924px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#eeeeee;fill-opacity:1;stroke:none;font-family:FreeSerif;-inkscape-font-specification:FreeSerif"
+         x="30.829479"
+         y="1042.3079"
+         id="text4376"
+         sodipodi:linespacing="125%"><tspan
+           sodipodi:role="line"
+           id="tspan4378"
+           x="30.829479"
+           y="1042.3079">Pb</tspan></text>
+    </g>
+    <g
+       id="g4356"
+       transform="matrix(0.15234083,0,0,0.15234083,-5.9011556,943.3707)"
+       style="opacity:1">
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.35245900000000002;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707000000000;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4641);enable-background:accumulate"
+         id="path4358"
+         sodipodi:cx="63.57143"
+         sodipodi:cy="683.07648"
+         sodipodi:rx="32.142857"
+         sodipodi:ry="15"
+         d="m 95.714287,683.07648 c 0,8.28427 -14.390847,15 -32.142857,15 -17.752009,0 -32.142856,-6.71573 -32.142856,-15 0,-8.28427 14.390847,-15 32.142856,-15 17.75201,0 32.142857,6.71573 32.142857,15 z"
+         transform="matrix(0.83309894,-0.60922647,0.70597542,0.69306694,-284.83936,153.03103)" />
+      <path
+         style="color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.35433071999999999;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4480);enable-background:accumulate"
+         d="m 219.28571,665.21933 c 66.78572,27.14286 107.85715,-14.28572 124.28572,-68.57143 32.14286,86.42857 -77.85715,135 -124.28572,68.57143 z"
+         id="path4360"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="ccc"
+         mask="url(#mask3953)" />
+    </g>
+    <path
+       sodipodi:nodetypes="ccccc"
+       inkscape:connector-curvature="0"
+       id="path4362"
+       d="m 570.23111,600.31891 10.60661,-12.6269 30.68927,36.94111 -10.8017,9.82617 z"
+       style="color:#000000;fill:url(#linearGradient4382);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4124);enable-background:accumulate"
+       transform="matrix(0.15234083,0,0,0.15234083,-59.761767,943.67847)"
+       inkscape:export-filename="/home/petteri/svn-jpa/nanopb/docs/text4764.png"
+       inkscape:export-xdpi="66.074806"
+       inkscape:export-ydpi="66.074806" />
+    <path
+       style="color:#000000;fill:url(#linearGradient4403);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4192-5-8);enable-background:accumulate"
+       d="m 459.07101,491.18655 10.6679,-13.40246 39.92639,39.30492 -7.75997,7.26977 z"
+       id="path4202-2-0"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc"
+       transform="matrix(0.15234083,0,0,0.15234083,-59.761767,943.67847)" />
+    <path
+       clip-path="url(#clipPath4804)"
+       transform="matrix(1.6507431,1.2720788,-1.2466097,1.657062,1.7389029,933.14042)"
+       d="m 32.393744,29.196428 a 1.3223162,2.5735531 0 1 1 -2.644632,0 1.3223162,2.5735531 0 1 1 2.644632,0 z"
+       sodipodi:ry="2.5735531"
+       sodipodi:rx="1.3223162"
+       sodipodi:cy="29.196428"
+       sodipodi:cx="31.071428"
+       id="path4798"
+       style="opacity:0.45081967;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6.21632814;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4738);enable-background:accumulate"
+       sodipodi:type="arc" />
+    <g
+       style="opacity:0.98000004;fill:#333333"
+       transform="matrix(0.12043176,0,0,0.12043176,-20.43703,941.11723)"
+       id="g4364">
+      <path
+         transform="matrix(0.68865217,-0.50491892,0.58356994,0.5744048,-198.94895,219.24158)"
+         d="m 95.714287,683.07648 c 0,8.28427 -14.390847,15 -32.142857,15 -17.752009,0 -32.142856,-6.71573 -32.142856,-15 0,-8.28427 14.390847,-15 32.142856,-15 17.75201,0 32.142857,6.71573 32.142857,15 z"
+         sodipodi:ry="15"
+         sodipodi:rx="32.142857"
+         sodipodi:cy="683.07648"
+         sodipodi:cx="63.57143"
+         id="path4366"
+         style="opacity:0.31557378;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4649);enable-background:accumulate"
+         sodipodi:type="arc" />
+      <path
+         mask="url(#mask3953)"
+         sodipodi:nodetypes="ccc"
+         inkscape:connector-curvature="0"
+         id="path4368"
+         d="m 218.26583,662.15968 c 84.07219,31.02539 107.45764,-22.43928 116.12665,-82.8498 47.44111,99.17711 -51.34018,183.44447 -116.12665,82.8498 z"
+         style="color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4661);enable-background:accumulate" />
+    </g>
+    <path
+       clip-path="url(#clipPath4507-9-7)"
+       id="path4665"
+       d="m 263.40625,861.375 c -41.40153,0 -74.9375,33.56722 -74.9375,74.96875 0,41.40153 33.53597,74.96875 74.9375,74.96875 41.40153,0 74.96875,-33.56722 74.96875,-74.96875 0,-41.40153 -33.56722,-74.96875 -74.96875,-74.96875 z m 0,3.53125 c 39.44891,0 71.4375,31.98859 71.4375,71.4375 0,39.44891 -31.98859,71.43745 -71.4375,71.43745 -39.44891,0 -71.40625,-31.98854 -71.40625,-71.43745 0,-39.44891 31.95734,-71.4375 71.40625,-71.4375 z"
+       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4625);enable-background:accumulate"
+       inkscape:connector-curvature="0"
+       transform="matrix(0.15234083,0,0,0.15234083,-3.6001409,895.98861)"
+       inkscape:export-filename="/home/petteri/svn-jpa/nanopb/docs/text4764.png"
+       inkscape:export-xdpi="66.074806"
+       inkscape:export-ydpi="66.074806" />
+    <path
+       transform="matrix(0.12009891,0,0,0.12009891,-18.652602,903.86961)"
+       inkscape:connector-curvature="0"
+       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4625);enable-background:accumulate"
+       d="m 263.40625,861.375 c -41.40153,0 -74.9375,33.56722 -74.9375,74.96875 0,41.40153 33.53597,74.96875 74.9375,74.96875 41.40153,0 74.96875,-33.56722 74.96875,-74.96875 0,-41.40153 -33.56722,-74.96875 -74.96875,-74.96875 z m 0,3.53125 c 39.44891,0 71.4375,31.98859 71.4375,71.4375 0,39.44891 -31.98859,71.43745 -71.4375,71.43745 -39.44891,0 -71.40625,-31.98854 -71.40625,-71.43745 0,-39.44891 31.95734,-71.4375 71.40625,-71.4375 z"
+       id="path4667"
+       clip-path="url(#clipPath4507-9-7)" />
+    <path
+       transform="matrix(0.15234083,0,0,-0.15234083,-76.298336,1107.5559)"
+       style="color:#000000;fill:url(#linearGradient4820);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4914);enable-background:accumulate"
+       d="m 577.55725,597.53928 -1.70131,-13.07078 c 0,0 33.61973,-17.56543 45.63461,-15.80715 0,0 2.94201,5.35937 1.76983,14.4438 -15.53143,-1.75828 -45.70313,14.43413 -45.70313,14.43413 z"
+       id="path4818"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.45081967;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6.21632814;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4738);enable-background:accumulate"
+       id="path4669"
+       sodipodi:cx="31.071428"
+       sodipodi:cy="29.196428"
+       sodipodi:rx="2.3400502"
+       sodipodi:ry="2.8629718"
+       d="m 33.411479,29.196428 a 2.3400502,2.8629718 0 1 1 -4.680101,0 2.3400502,2.8629718 0 1 1 4.680101,0 z"
+       transform="matrix(3.1362406,0,0,3.110622,-70.33384,935.46713)"
+       clip-path="url(#clipPath4744)"
+       inkscape:export-filename="/home/petteri/svn-jpa/nanopb/docs/text4764.png"
+       inkscape:export-xdpi="66.074806"
+       inkscape:export-ydpi="66.074806" />
+    <path
+       style="color:#000000;fill:url(#radialGradient4384);fill-opacity:1;stroke:none;stroke-width:0.35433071999999999;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="m 12.311735,1016.9223 c 1.550635,4.0379 10.914009,12.0675 16.063795,15.8189 1.507517,1.0981 -0.06039,3.1767 -1.414591,2.2851 -5.838796,-3.8444 -16.020602,-13.7073 -17.0431309,-16.88 -0.6363502,-1.9743 1.8481339,-2.6454 2.3939269,-1.224 z"
+       id="path4370"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="sssss" />
+    <text
+       sodipodi:linespacing="125%"
+       id="text4372"
+       y="1042.2502"
+       x="1.0272956"
+       style="font-size:8.53108596999999946px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Baroque Script;-inkscape-font-specification:Baroque Script;stroke-opacity:1;stroke-width:0.29999999999999999;stroke-miterlimit:4;stroke-dasharray:none"
+       xml:space="preserve"><tspan
+         y="1042.2502"
+         x="1.0272956"
+         id="tspan4374"
+         sodipodi:role="line"
+         dx="0 0.089285716 -0.40178573 0.17857142">nano</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:5.24954605000000019px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient4776);fill-opacity:1;stroke:none;font-family:Baroque Script;-inkscape-font-specification:Baroque Script;filter:url(#filter4794)"
+       x="-16.312414"
+       y="641.44263"
+       id="text4764"
+       sodipodi:linespacing="125%"
+       transform="matrix(-0.61516641,0.01474994,0.03895428,1.6246424,0,0)"
+       inkscape:export-xdpi="66.074806"
+       inkscape:export-ydpi="66.074806"><tspan
+         dx="0 0.05494136 -0.24723613 0.10988271"
+         sodipodi:role="line"
+         id="tspan4766"
+         x="-16.312414"
+         y="641.44263"
+         style="fill:url(#linearGradient4776);fill-opacity:1">nano</tspan></text>
+    <path
+       sodipodi:nodetypes="sssss"
+       inkscape:connector-curvature="0"
+       id="path4808"
+       d="m 12.311735,1016.9223 c 1.550635,4.0379 10.914009,12.0675 16.063795,15.8189 1.507517,1.0981 -0.06039,3.1767 -1.414591,2.2851 -5.838796,-3.8444 -16.020602,-13.7073 -17.0431309,-16.88 -0.6363502,-1.9743 1.8481339,-2.6454 2.3939269,-1.224 z"
+       style="color:#000000;fill:url(#radialGradient4816);fill-opacity:1;stroke:none;stroke-width:0.35433071999999999;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="Taso#1"
+     style="display:none"
+     transform="translate(0,-2)">
+    <path
+       transform="translate(0,-1002.3622)"
+       style="color:#000000;fill:url(#radialGradient4116);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="m -35.379815,1018.4927 c 2.32122,0.8511 12.114835,9.3296 16.79371,13.7454 1.394403,1.316 -0.01718,2.6465 -1.286446,1.5661 -4.380417,-3.7284 -11.292967,-10.036 -16.625913,-13.9411 -1.720605,-1.26 -0.350832,-1.9092 1.118649,-1.3704 z"
+       id="path3794"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="sssss" />
+    <path
+       transform="matrix(0.21843081,0,0,0.21843081,-118.38007,-122.81195)"
+       sodipodi:type="arc"
+       style="color:#000000;fill:#141414;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.45699024;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="path3003"
+       sodipodi:cx="352.14285"
+       sodipodi:cy="623.07648"
+       sodipodi:rx="40.714287"
+       sodipodi:ry="40.714287"
+       d="m 392.85714,623.07648 c 0,22.48588 -18.22841,40.71428 -40.71429,40.71428 -22.48588,0 -40.71428,-18.2284 -40.71428,-40.71428 0,-22.48588 18.2284,-40.71429 40.71428,-40.71429 22.48588,0 40.71429,18.22841 40.71429,40.71429 z" />
+    <path
+       transform="matrix(0.27475574,0,0,0.27475574,-114.10762,-135.03034)"
+       d="m 392.85714,623.07648 c 0,22.48588 -18.22841,40.71428 -40.71429,40.71428 -22.48588,0 -40.71428,-18.2284 -40.71428,-40.71428 0,-22.48588 18.2284,-40.71429 40.71428,-40.71429 22.48588,0 40.71429,18.22841 40.71429,40.71429 z"
+       sodipodi:ry="40.714287"
+       sodipodi:rx="40.714287"
+       sodipodi:cy="623.07648"
+       sodipodi:cx="352.14285"
+       id="path3001"
+       style="color:#000000;fill:#1b1b1b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       sodipodi:type="arc" />
+    <g
+       id="g3957"
+       transform="matrix(0.15661078,0,0,0.15661078,-60.981634,-61.75258)">
+      <path
+         transform="matrix(0.83309894,-0.60922647,0.70597542,0.69306694,-284.83936,153.03103)"
+         d="m 95.714287,683.07648 c 0,8.28427 -14.390847,15 -32.142857,15 -17.752009,0 -32.142856,-6.71573 -32.142856,-15 0,-8.28427 14.390847,-15 32.142856,-15 17.75201,0 32.142857,6.71573 32.142857,15 z"
+         sodipodi:ry="15"
+         sodipodi:rx="32.142857"
+         sodipodi:cy="683.07648"
+         sodipodi:cx="63.57143"
+         id="path3865"
+         style="color:#000000;fill:#282828;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4049);enable-background:accumulate"
+         sodipodi:type="arc" />
+      <path
+         mask="url(#mask3953)"
+         sodipodi:nodetypes="ccc"
+         inkscape:connector-curvature="0"
+         id="path3863"
+         d="m 219.28571,665.21933 c 66.78572,27.14286 107.85715,-14.28572 124.28572,-68.57143 32.14286,86.42857 -77.85715,135 -124.28572,68.57143 z"
+         style="color:#000000;fill:#030303;fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4077);enable-background:accumulate" />
+    </g>
+    <path
+       style="color:#000000;fill:url(#linearGradient4134);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4124);enable-background:accumulate"
+       d="m 216.67772,602.33922 10.60661,-12.6269 38.38578,25.25381 -9.09136,16.66752 z"
+       id="path4118"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc"
+       transform="matrix(0.15661078,0,0,0.15661078,-60.981634,-61.75258)" />
+    <g
+       id="g3961"
+       transform="matrix(0.1232991,0,0,0.1232991,-75.821959,-63.79082)"
+       style="fill:#333333">
+      <path
+         sodipodi:type="arc"
+         style="color:#000000;fill:#252525;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3979);enable-background:accumulate"
+         id="path3963"
+         sodipodi:cx="63.57143"
+         sodipodi:cy="683.07648"
+         sodipodi:rx="32.142857"
+         sodipodi:ry="15"
+         d="m 95.714287,683.07648 c 0,8.28427 -14.390847,15 -32.142857,15 -17.752009,0 -32.142856,-6.71573 -32.142856,-15 0,-8.28427 14.390847,-15 32.142856,-15 17.75201,0 32.142857,6.71573 32.142857,15 z"
+         transform="matrix(0.75135225,-0.54944696,0.63670255,0.6250607,-232.37743,195.04269)" />
+      <path
+         style="color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3937);enable-background:accumulate"
+         d="M 219.28571,665.21933 C 285,705.93362 343.57143,661.6479 343.57143,596.6479 c 32.14286,86.42857 -77.85715,135 -124.28572,68.57143 z"
+         id="path3965"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="ccc"
+         mask="url(#mask3953)" />
+    </g>
+    <path
+       transform="translate(0,-1002.3622)"
+       sodipodi:nodetypes="sssss"
+       inkscape:connector-curvature="0"
+       id="path3792"
+       d="m -42.258257,1016.2227 c 1.5941,4.1511 11.219916,12.4058 16.514047,16.2623 1.549768,1.1289 -0.06211,3.2658 -1.454242,2.3492 -6.002457,-3.9522 -16.469643,-14.0915 -17.520831,-17.3531 -0.654184,-2.0297 1.899933,-2.7195 2.461026,-1.2584 z"
+       style="color:#000000;fill:url(#radialGradient4108);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <text
+       transform="translate(0,-1002.3622)"
+       xml:space="preserve"
+       style="font-size:8.77020359px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Baroque Script;-inkscape-font-specification:Baroque Script"
+       x="-54.180252"
+       y="1042.3066"
+       id="text3847"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3849"
+         x="-54.180252"
+         y="1042.3066">nano</tspan></text>
+    <text
+       transform="translate(0,-1002.3622)"
+       xml:space="preserve"
+       style="font-size:11.27597618px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#eeeeee;fill-opacity:1;stroke:none;font-family:FreeSerif;-inkscape-font-specification:FreeSerif"
+       x="-23.221466"
+       y="1042.3199"
+       id="text3851"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3853"
+         x="-23.221466"
+         y="1042.3199">Pb</tspan></text>
+    <path
+       transform="translate(0,-1002.3622)"
+       sodipodi:nodetypes="sssss"
+       inkscape:connector-curvature="0"
+       id="path4136"
+       d="m -37.752827,1067.2187 c 2.321221,0.851 12.114834,9.3295 16.79371,13.7453 1.394404,1.3161 -0.01718,2.6464 -1.286445,1.5661 -4.380418,-3.7284 -11.292968,-10.036 -16.625914,-13.9411 -1.720604,-1.26 -0.350831,-1.9091 1.118649,-1.3703 z"
+       style="color:#000000;fill:url(#radialGradient4166);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <path
+       d="m 392.85714,623.07648 c 0,22.48588 -18.22841,40.71428 -40.71429,40.71428 -22.48588,0 -40.71428,-18.2284 -40.71428,-40.71428 0,-22.48588 18.2284,-40.71429 40.71428,-40.71429 22.48588,0 40.71429,18.22841 40.71429,40.71429 z"
+       sodipodi:ry="40.714287"
+       sodipodi:rx="40.714287"
+       sodipodi:cy="623.07648"
+       sodipodi:cx="352.14285"
+       id="path4138"
+       style="color:#000000;fill:#ededed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.45699024;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       sodipodi:type="arc"
+       transform="matrix(0.21745941,0,0,0.21745941,-120.45056,-73.52041)" />
+    <path
+       sodipodi:type="arc"
+       style="color:#000000;fill:#ededed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="path4140"
+       sodipodi:cx="352.14285"
+       sodipodi:cy="623.07648"
+       sodipodi:rx="40.714287"
+       sodipodi:ry="40.714287"
+       d="m 392.85714,623.07648 c 0,22.48588 -18.22841,40.71428 -40.71429,40.71428 -22.48588,0 -40.71428,-18.2284 -40.71428,-40.71428 0,-22.48588 18.2284,-40.71429 40.71428,-40.71429 22.48588,0 40.71429,18.22841 40.71429,40.71429 z"
+       transform="matrix(0.27475574,0,0,0.27475574,-116.48063,-86.30449)" />
+    <g
+       id="g4142"
+       transform="matrix(0.15661078,0,0,0.15661078,-63.354645,-13.02673)">
+      <path
+         sodipodi:type="arc"
+         style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4172);enable-background:accumulate"
+         id="path4144"
+         sodipodi:cx="63.57143"
+         sodipodi:cy="683.07648"
+         sodipodi:rx="32.142857"
+         sodipodi:ry="15"
+         d="m 95.714287,683.07648 c 0,8.28427 -14.390847,15 -32.142857,15 -17.752009,0 -32.142856,-6.71573 -32.142856,-15 0,-8.28427 14.390847,-15 32.142856,-15 17.75201,0 32.142857,6.71573 32.142857,15 z"
+         transform="matrix(0.83309894,-0.60922647,0.70597542,0.69306694,-284.83936,153.03103)" />
+      <path
+         style="color:#000000;fill:#c2c2c2;fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4077);enable-background:accumulate"
+         d="m 219.28571,665.21933 c 66.78572,27.14286 107.85715,-14.28572 124.28572,-68.57143 32.14286,86.42857 -77.85715,135 -124.28572,68.57143 z"
+         id="path4146"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="ccc"
+         mask="url(#mask3953)" />
+    </g>
+    <path
+       transform="matrix(0.15661078,0,0,0.15661078,-61.013712,-61.76127)"
+       inkscape:connector-curvature="0"
+       style="color:#000000;fill:#a0a0a0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4625);enable-background:accumulate"
+       d="m 263.40625,861.375 c -41.40153,0 -74.9375,33.56722 -74.9375,74.96875 0,41.40153 33.53597,74.96875 74.9375,74.96875 41.40153,0 74.96875,-33.56722 74.96875,-74.96875 0,-41.40153 -33.56722,-74.96875 -74.96875,-74.96875 z m 0,3.53125 c 39.44891,0 71.4375,31.98859 71.4375,71.4375 0,39.44891 -31.98859,71.43745 -71.4375,71.43745 -39.44891,0 -71.40625,-31.98854 -71.40625,-71.43745 0,-39.44891 31.95734,-71.4375 71.40625,-71.4375 z"
+       id="path4494-3-1"
+       clip-path="url(#clipPath4507-9-7)" />
+    <g
+       style="fill:#333333"
+       transform="matrix(0.12363792,0,0,0.12363792,-78.263621,-15.25058)"
+       id="g4150">
+      <path
+         transform="matrix(0.75135225,-0.54944696,0.63670255,0.6250607,-232.37743,195.04269)"
+         d="m 95.714287,683.07648 c 0,8.28427 -14.390847,15 -32.142857,15 -17.752009,0 -32.142856,-6.71573 -32.142856,-15 0,-8.28427 14.390847,-15 32.142856,-15 17.75201,0 32.142857,6.71573 32.142857,15 z"
+         sodipodi:ry="15"
+         sodipodi:rx="32.142857"
+         sodipodi:cy="683.07648"
+         sodipodi:cx="63.57143"
+         id="path4152"
+         style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3979);enable-background:accumulate"
+         sodipodi:type="arc" />
+      <path
+         mask="url(#mask3953)"
+         sodipodi:nodetypes="ccc"
+         inkscape:connector-curvature="0"
+         id="path4154"
+         d="M 219.28571,665.21933 C 285,705.93362 343.57143,661.6479 343.57143,596.6479 c 32.14286,86.42857 -77.85715,135 -124.28572,68.57143 z"
+         style="color:#000000;fill:#a1a1a1;fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3937);enable-background:accumulate" />
+    </g>
+    <path
+       style="opacity:0.29098361;color:#000000;fill:url(#linearGradient4204);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4192);enable-background:accumulate"
+       d="m 89.653056,803.08367 12.096464,-10.90246 39.39593,41.08013 -9.27519,7.18311 z"
+       id="path4202"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc"
+       transform="matrix(0.15661078,0,0,0.15661078,-60.981634,-61.75258)" />
+    <text
+       transform="translate(0,-1002.3622)"
+       sodipodi:linespacing="125%"
+       id="text4158"
+       y="1091.0328"
+       x="-56.553265"
+       style="font-size:8.77020359px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Baroque Script;-inkscape-font-specification:Baroque Script"
+       xml:space="preserve"><tspan
+         y="1091.0328"
+         x="-56.553265"
+         id="tspan4160"
+         sodipodi:role="line">nano</tspan></text>
+    <text
+       transform="translate(0,-1002.3622)"
+       sodipodi:linespacing="125%"
+       id="text4162"
+       y="1091.0461"
+       x="-25.594479"
+       style="font-size:11.27597618px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#4f4f4f;fill-opacity:1;stroke:none;font-family:FreeSerif;-inkscape-font-specification:FreeSerif"
+       xml:space="preserve"><tspan
+         y="1091.0461"
+         x="-25.594479"
+         id="tspan4164"
+         sodipodi:role="line"
+         style="fill:#4f4f4f;fill-opacity:1;stroke:none">Pb</tspan></text>
+    <g
+       transform="matrix(0.15661078,0,0,0.15661078,-63.354645,-13.02673)"
+       id="g4541">
+      <path
+         transform="matrix(0.83309894,-0.60922647,0.70597542,0.69306694,-284.83936,153.03103)"
+         d="m 95.714287,683.07648 c 0,8.28427 -14.390847,15 -32.142857,15 -17.752009,0 -32.142856,-6.71573 -32.142856,-15 0,-8.28427 14.390847,-15 32.142856,-15 17.75201,0 32.142857,6.71573 32.142857,15 z"
+         sodipodi:ry="15"
+         sodipodi:rx="32.142857"
+         sodipodi:cy="683.07648"
+         sodipodi:cx="63.57143"
+         id="path4543"
+         style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4172);enable-background:accumulate"
+         sodipodi:type="arc" />
+      <path
+         mask="url(#mask3953)"
+         sodipodi:nodetypes="ccc"
+         inkscape:connector-curvature="0"
+         id="path4545"
+         d="m 219.28571,665.21933 c 66.78572,27.14286 107.85715,-14.28572 124.28572,-68.57143 32.14286,86.42857 -77.85715,135 -124.28572,68.57143 z"
+         style="color:#000000;fill:#c2c2c2;fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4077);enable-background:accumulate" />
+    </g>
+    <path
+       sodipodi:nodetypes="ccccc"
+       inkscape:connector-curvature="0"
+       id="path4148"
+       d="m 200.0102,910.94082 9.59646,-11.61675 39.39593,29.29442 -6.0609,11.11168 z"
+       style="opacity:0.29098361;color:#000000;fill:url(#linearGradient4750);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4192);enable-background:accumulate"
+       transform="matrix(0.15661078,0,0,0.15661078,-60.981634,-61.75258)" />
+    <path
+       transform="translate(0,-1002.3622)"
+       style="color:#000000;fill:url(#radialGradient4170);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="m -44.631268,1064.9485 c 1.594098,4.1512 11.219915,12.406 16.514047,16.2625 1.549767,1.1288 -0.06211,3.2657 -1.454242,2.349 -6.002458,-3.952 -16.469642,-14.0913 -17.520832,-17.3529 -0.654182,-2.0298 1.899934,-2.7196 2.461027,-1.2586 z"
+       id="path4156"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="sssss" />
+    <path
+       transform="translate(0,-1002.3622)"
+       sodipodi:nodetypes="sssss"
+       inkscape:connector-curvature="0"
+       id="path4258"
+       d="m -34.484897,966.58744 c 2.321221,0.85108 12.114835,9.32955 16.79371,13.74539 1.394405,1.31601 -0.01717,2.64647 -1.286444,1.5661 -4.380418,-3.72845 -11.292968,-10.03607 -16.625915,-13.94115 -1.720603,-1.25992 -0.35083,-1.90914 1.118649,-1.37034 z"
+       style="color:#000000;fill:url(#radialGradient4288);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <path
+       transform="translate(0,-1002.3622)"
+       style="opacity:0.35655739;color:#000000;fill:url(#linearGradient4348);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="m -34.484897,966.58744 c 2.321221,0.85108 12.114835,9.32955 16.79371,13.74539 1.394405,1.31601 -0.01717,2.64647 -1.286444,1.5661 -4.380418,-3.72845 -11.292968,-10.03607 -16.625915,-13.94115 -1.720603,-1.25992 -0.35083,-1.90914 1.118649,-1.37034 z"
+       id="path4340"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="sssss" />
+    <path
+       d="m 392.85714,623.07648 c 0,22.48588 -18.22841,40.71428 -40.71429,40.71428 -22.48588,0 -40.71428,-18.2284 -40.71428,-40.71428 0,-22.48588 18.2284,-40.71429 40.71428,-40.71429 22.48588,0 40.71429,18.22841 40.71429,40.71429 z"
+       sodipodi:ry="40.714287"
+       sodipodi:rx="40.714287"
+       sodipodi:cy="623.07648"
+       sodipodi:cx="352.14285"
+       id="path4260"
+       style="color:#000000;fill:#6c6c6c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.45699024;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       sodipodi:type="arc"
+       transform="matrix(0.21843081,0,0,0.21843081,-117.48515,-174.71724)" />
+    <path
+       sodipodi:type="arc"
+       style="color:#000000;fill:#727272;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="path4262"
+       sodipodi:cx="352.14285"
+       sodipodi:cy="623.07648"
+       sodipodi:rx="40.714287"
+       sodipodi:ry="40.714287"
+       d="m 392.85714,623.07648 c 0,22.48588 -18.22841,40.71428 -40.71429,40.71428 -22.48588,0 -40.71428,-18.2284 -40.71428,-40.71428 0,-22.48588 18.2284,-40.71429 40.71428,-40.71429 22.48588,0 40.71429,18.22841 40.71429,40.71429 z"
+       transform="matrix(0.27475574,0,0,0.27475574,-113.2127,-186.93561)" />
+    <g
+       id="g4264"
+       transform="matrix(0.15661078,0,0,0.15661078,-60.086715,-113.65786)">
+      <path
+         sodipodi:type="arc"
+         style="color:#000000;fill:#9c9c9c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4049);enable-background:accumulate"
+         id="path4266"
+         sodipodi:cx="63.57143"
+         sodipodi:cy="683.07648"
+         sodipodi:rx="32.142857"
+         sodipodi:ry="15"
+         d="m 95.714287,683.07648 c 0,8.28427 -14.390847,15 -32.142857,15 -17.752009,0 -32.142856,-6.71573 -32.142856,-15 0,-8.28427 14.390847,-15 32.142856,-15 17.75201,0 32.142857,6.71573 32.142857,15 z"
+         transform="matrix(0.83309894,-0.60922647,0.70597542,0.69306694,-284.83936,153.03103)" />
+      <path
+         style="color:#000000;fill:#4b4b4b;fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4077);enable-background:accumulate"
+         d="m 219.28571,665.21933 c 66.78572,27.14286 107.85715,-14.28572 124.28572,-68.57143 32.14286,86.42857 -77.85715,135 -124.28572,68.57143 z"
+         id="path4268"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="ccc"
+         mask="url(#mask3953)" />
+    </g>
+    <path
+       sodipodi:nodetypes="ccccc"
+       inkscape:connector-curvature="0"
+       id="path4270"
+       d="m 222.39201,270.91065 10.60661,-12.6269 38.38578,25.25381 -9.09136,16.66752 z"
+       style="color:#000000;fill:url(#linearGradient4290);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4124);enable-background:accumulate"
+       transform="matrix(0.15661078,0,0,0.15661078,-60.981634,-61.75258)" />
+    <g
+       style="fill:#333333"
+       transform="matrix(0.1232991,0,0,0.1232991,-74.927039,-115.6961)"
+       id="g4272">
+      <path
+         transform="matrix(0.75135225,-0.54944696,0.63670255,0.6250607,-232.37743,195.04269)"
+         d="m 95.714287,683.07648 c 0,8.28427 -14.390847,15 -32.142857,15 -17.752009,0 -32.142856,-6.71573 -32.142856,-15 0,-8.28427 14.390847,-15 32.142856,-15 17.75201,0 32.142857,6.71573 32.142857,15 z"
+         sodipodi:ry="15"
+         sodipodi:rx="32.142857"
+         sodipodi:cy="683.07648"
+         sodipodi:cx="63.57143"
+         id="path4274"
+         style="color:#000000;fill:#acacac;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3979);enable-background:accumulate"
+         sodipodi:type="arc" />
+      <path
+         mask="url(#mask3953)"
+         sodipodi:nodetypes="ccc"
+         inkscape:connector-curvature="0"
+         id="path4276"
+         d="M 219.28571,665.21933 C 285,705.93362 343.57143,661.6479 343.57143,596.6479 c 32.14286,86.42857 -77.85715,135 -124.28572,68.57143 z"
+         style="color:#000000;fill:#414141;fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3937);enable-background:accumulate" />
+    </g>
+    <path
+       style="opacity:0.71672136;color:#000000;fill:url(#linearGradient4317);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4192-5);enable-background:accumulate"
+       d="m 112.11094,164.13302 10.6679,-13.40246 39.92639,39.30492 -7.75997,7.26977 z"
+       id="path4202-2"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc"
+       transform="matrix(0.15661078,0,0,0.15661078,-60.981634,-61.75258)" />
+    <path
+       transform="matrix(0.15661078,0,0,0.15661078,-57.781488,-162.39201)"
+       inkscape:connector-curvature="0"
+       style="color:#000000;fill:#434343;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4501-0);enable-background:accumulate"
+       d="m 263.40625,861.375 c -41.40153,0 -74.9375,33.56722 -74.9375,74.96875 0,41.40153 33.53597,74.96875 74.9375,74.96875 41.40153,0 74.96875,-33.56722 74.96875,-74.96875 0,-41.40153 -33.56722,-74.96875 -74.96875,-74.96875 z m 0,3.53125 c 39.44891,0 71.4375,31.98859 71.4375,71.4375 0,39.44891 -31.98859,71.43745 -71.4375,71.43745 -39.44891,0 -71.40625,-31.98854 -71.40625,-71.43745 0,-39.44891 31.95734,-71.4375 71.40625,-71.4375 z"
+       id="path4494-3"
+       clip-path="url(#clipPath4507-9)" />
+    <path
+       transform="translate(0,-1002.3622)"
+       style="color:#000000;fill:url(#radialGradient4292);fill-opacity:1;stroke:none;stroke-width:0.35433072;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="m -41.363336,964.31737 c 1.594097,4.15111 11.219915,12.40584 16.514046,16.26235 1.549766,1.12894 -0.06211,3.26578 -1.454243,2.34916 -6.002456,-3.95218 -16.469642,-14.09147 -17.520831,-17.35303 -0.654184,-2.02975 1.899932,-2.71959 2.461028,-1.25848 z"
+       id="path4278"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="sssss" />
+    <text
+       transform="translate(0,-1002.3622)"
+       sodipodi:linespacing="125%"
+       id="text4280"
+       y="990.40137"
+       x="-53.285336"
+       style="font-size:8.77020359px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Baroque Script;-inkscape-font-specification:Baroque Script"
+       xml:space="preserve"><tspan
+         y="990.40137"
+         x="-53.285336"
+         id="tspan4282"
+         sodipodi:role="line">nano</tspan></text>
+    <text
+       transform="translate(0,-1002.3622)"
+       sodipodi:linespacing="125%"
+       id="text4284"
+       y="990.41461"
+       x="-22.326546"
+       style="font-size:11.27597618px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#eeeeee;fill-opacity:1;stroke:none;font-family:FreeSerif;-inkscape-font-specification:FreeSerif"
+       xml:space="preserve"><tspan
+         y="990.41461"
+         x="-22.326546"
+         id="tspan4286"
+         sodipodi:role="line">Pb</tspan></text>
+    <path
+       clip-path="url(#clipPath4507-9-7)"
+       id="path4629"
+       d="m 263.40625,861.375 c -41.40153,0 -74.9375,33.56722 -74.9375,74.96875 0,41.40153 33.53597,74.96875 74.9375,74.96875 41.40153,0 74.96875,-33.56722 74.96875,-74.96875 0,-41.40153 -33.56722,-74.96875 -74.96875,-74.96875 z m 0,3.53125 c 39.44891,0 71.4375,31.98859 71.4375,71.4375 0,39.44891 -31.98859,71.43745 -71.4375,71.43745 -39.44891,0 -71.40625,-31.98854 -71.40625,-71.43745 0,-39.44891 31.95734,-71.4375 71.40625,-71.4375 z"
+       style="color:#000000;fill:#a0a0a0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4625);enable-background:accumulate"
+       inkscape:connector-curvature="0"
+       transform="matrix(0.12349317,0,0,0.12349317,-76.338029,-53.66762)" />
+  </g>
+  <g
+     sodipodi:insensitive="true"
+     inkscape:label="Taso"
+     id="layer2"
+     inkscape:groupmode="layer"
+     style="display:none"
+     transform="translate(0,-1004.3622)">
+    <image
+       style="opacity:0.61065572"
+       width="415"
+       height="330"
+       xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZ8AAAFKCAIAAABNc8VfAAAAA3NCSVQICAjb4U/gAAAgAElEQVR4 nO292Y8kR57n9zOPiIyMPD0z68o6WDybXewedkPHNji70GAGPeB0L7CPetQ/sW/7pP9Af4AAAYKg h33QiyRAmp0ZzQJaaGY0rUGTzeFVB6vIJItZeUVkZtwRbnqwCEsLu9zc3NzcPCq/IMCszDjMzM0/ /v397EIfffQRXOs1VhzHCCEAaLfbGGPpC9rttvdyLRQAIXR2dpZa1KILkOn1rgqZ9dvNPxMMWrKI bzfRzs4O+SFPM6Jrur22Mr8Jy+riVKSve4YaJxPKu+Wa+Mn5L4FdCX0+4bgS5qn4Nd1eO1k7IP8m jitq6ZBVFcCbqcxzCXKS10Pja0poV/Frur1Gyu8svAHOeUd3KLYAxZk1zbdnpYzDQhbEOJMSWnz1 Nd2WX26dRdHP8II6uluVlQHkCpDaAgXB1237x3Hc6XQMS5jpq6/ptswqNA3k1kBZ8KIsE0dbNY5j ACgXsqoW8GAq87e/dSENGXdNtyWUH2dRbp7bbRnMvwuEVi03UhZbwGewbN3+TgqZ+u3XdFsqlZIG sru3HSK4aL6ktmo4kTKUESxnqr7zLqr59mu6LYPKTQNlhUsRCC4x282+uCwT52R2WE6lXoJCH73S b7+mW4VVem6bK0lZSW72813xxXpemGcT53B2mMPycAXwFlJw335Nt0pK7NPlTpKgpZIWwyeFc97e Torq53LoJ82UzjjSAv5TJey3X9OtStLce+EAjr2vSuncYNUa4S+f4j7cZBFV6cMd5c6YuaZbNVSJ WWBcSSDsNBD3ypCXT3EfmGnSjP8uwRay3D55TbegVaFZYPTb6Vyw0rOBkNYafvyFkytSiXkzqkKW xbhrugWqnL3ZP+DEAgfiJcvNc2vKYP5GCH7eTJixxTXdwpKr3uz5cQ0Bb6DEFqNCU2eqMm/GIlj2 1h+u6RaECrrrSn9cs68s18SFMCMMzJrCw7wZV5cjzrJEtIgC6HVNt5JVoSlg9AOrsoES/d7QZs+o bu9KLKJi355z3kzRjLumWznyPAXMyf3spEP7JEtoSW5ObGtcT53J/1Hix17TzbfKmt9Y7oPaVUnM vwLSnhzhmDi4njpTzCKTa7p5UiD92GJBKBRQ5oLIUonpYNy3k7GOEIykpik8PJKdXAsuD3hNt8JV 7qRtsTAmWPHTmx0CrloTaEKePcM1RbWmznDlvKabY3300Z8ihBCqp77y+OQEY3j89T+Fs1+NZ4Pp 6nGd//Yrfb6r52JoRMtQ4lM5UztoynlNNwf66KM/XV/fID+j+TZb/f5Q9fpGY4X8cHJyyl6S4Wjo DXbcU7rcrmxhnYoAcTgTaMpNCFZl9kxqk17TzUYszoAhGgA0m6v05263n/pRnc4F+8/xeER/Pjo6 /vHHxwDFxoYhBM6untVOSuKcLBYFLsvEhTl7RloGk6l213TLpr/4i3+DEJpMFtqUJRonDeCiWo38 0G6fAwBOEvE1xycnyXRycPClc8aFMMrBKfVe8gNit5Nd8xTYJ1xCnj3DlcG8Va/pZqR/9a/+bHNz E+Y2bTxONERjFdVqJg6OAE6qo6MjAJxMp5PJ5Mcfn+THnPh8Lr37Umke1J4NZk6yuCqwtwk0oS0R VZUBsjyPr+mWImLWaOzZbLbID+PxVPUWasqocgIOAI6PTzBOCObsrJy+c4QQgxBZP6iLLkmmd1Vi RZ3FBJqyOgk7ewaMzyG7pptSKq5RcYATocZKBTiuX3XUjDs+OZm/ZQoAo+HIMCtn2InDARyEFDhn mkMDhRXYuXsyyVt5KIbhl3JFNbwo13STKJVrVFNJrkypbrdv0pd0gDs+nv2EEABgPP32xT9rAFet 2a1sGbI+pT0UqcSZroYlyfQhgS8RZb9IFW2kluGabgv6+ON/jRCKotlsNQ3XarWrGW2j8UT/sfTS mISooAbcdDo9OTmNIsT8Dl92eyfHz1jG5fQRZZk4aVcOx1GWPtNVUxLzN7pdIlr6BBo9467pdqWP P/7XURSRn1dXN1QvY7lGJQWc6rroGZdMEwBotzua15ycnNKfowh1e73jo6cA2GEy2ydTUue4hgO4 0me6ciXJ9PoiVtQ5N3EOJ9Bc0w1gbtmI31ltzf0a5vNoUq5RUcCZXBQRcIkQ5ZoDjnxtfzA4OX5a iamt9CsMPWYIITMRmelaejaQyOQaVWWJKP0oi2ygqhjXdFu0bK3FUHQOOD3XqIajlBCVVbfbF4km SmTceDwmP5yd8T17PB4nyfTk5Bsn8+OKY4rdLVeuiWPLHA5tU9fV+VwiGsgcGlqM15pucRz/6lf/ Uok2AMA1Q67RSzJSzxRhRbhmmIZrtzuUaJxEwE0mEwzJ0aunriYAu2VK/gmupYzZqWa6hhYys/8s JSEYwhwaWozXlG6Ea5JodFGNeiNJIv1HiZdEDzjRr+kZN51OAeD0VNdjjo9PhCLhV68cTP0lcnIb O+zE3rCSWuZwTBwEM43GvE2KXlT3etGNXv5/8S/+WGfZABr1Bv1ZBTjNJZECThOHSgFHuEYlBdxg OFurf3lxSX9J6pgkU7eAs7uNC7rligZcphsvBBNH2xmqMI0G8qXYDPW60I3trH/2Z38WRVGj0dJY NvGXHONMrgplnEl+jQKOgxonlnEUbUQEcOyS/iSZJjg5PnpWiokrOj4qyDdZ5wTLMnFigUOgLZG0 JN4C5yWnm2gcCNoAYHNrV/oWKdqICODML8l4PJlOM1y/8/PL9BcBnJ6ecVyj6l52mX/NHBzGydGR 1zSc5wmuru5kJ9NcA5lJE07IzJbEc0JwaekmbUeKtlZrDQDqDX4lvAZtAAAQmdNqPJ8gYvgWMnwh jhJwGo5GIJkOMlO/1weAZLbdyNzEIZhOxn4AV0o+Oz9T3OYEPWDFyWRXnyqlYywb3TRZHg5tRBRw aVwDgFlkakKr8eLkXv1bxGFZFeMI2qg4xhG0ESVJwtINMEynjgHH3TblTnDNmRYsYpprofP4M2Ws gppJ47MkS0K31NS1FG1E9caqOdqINLQaK1Zlqd6imnEiAo5DGxEFHIs2oiSZfyOhHIZpMjlyN84A i5nsECa4mt88HoYXA5nHX1xhDL9XXADvrSSVp5vJ9dagDQA2N7e43SgFScZMpbRSoU36rtSZdCzg pGgjOjk5FdEGANMkAQAEiNINADscSA1k/gEnk6G6Cs1xZT8nf7F9WqfU1XUeGGc0VTVAZbq12GFE TpubWwBQr/Pb7c6lnOxWqyEWValc495lMkl4ZycGgB8PX6V8ZiTZeWk63+kXA57hDQFgFEW1mzff yRmich03nOQOAJC7t9yhOq48OdvHYbHzF8ZEJgXWXCaHqp53y3qxNcaNoI1KAFzKPF6YezFDtBGN x+OGMJqhEqnj4asjzWvaZ7OlWpeXV0Ou08V9zGd8x+Qj8eHh13Z00w/SBTILgYgtT+mL3sGqfYor dkEXy+ECeCeqDN3s4qBf//rX5IdUtBExgEtHGwAMBkPzFwOzRNQEcGw1VYBrtzsspijgprJTGmiM inFyeJjhPBrDxg8QcEHFzoFM4s9aGPMPdLgA3okqQDfrK/3rX/86iqIkSQzRRjSZ4IxoI0p/i7hW VMM4sbIi4Di0EV1eXkrRRoTIAKrxSoasjR8O4GjJ4wBOemelbyL/acH8WHFVZuedJ9y8W/4HL3l7 JrQBwP7+7ZcvdZEg0SLaACDRA066DH48HkgBJ63v7Vs3gWGcFG0AsLGxcXp2qsrrYZwgFAFghFJw bNdl/WR29OJKTrdjC4RxqiYqKy0IObDitszOO0+I3s1JkxHjVq/XYTGFr0fbzZu3CBN/+EGXyxfQ RiVBhmp7DyoOcKm1Pnx1pEIb0enZKagGLuYfjhBSxaeVG55jv1Q/TheIrySi5QkkLZgJK4WW2RXj AvJurFmL4zhnq7HjpNPphNzqhmgDgLt3b6kAp0YbiA4uFW2w6OBMan371s1O+xwr8HbWnvUJWmt5 QZMEIXTr1rs0PnWbpSIXMZD5B6UUKVXtMI7KpoUB4wdAnhSb88JoVL53U91UeerGGjeqWq2uoRuL NioRcFq0sYrADG1Uvf5ga3Pb5JU//PCSNNTlZU/8KzFurBYYN29h8gkIoSSZTCanUEzqPZD5B56L ZF6MUmbw66Vvn2qlBcukW2pL2V11gjYA4Oi2vb2NkPxQPinaiFjAGaMNQHvgqahef0B+SAUcRRsR B7iz9pnYmBjjOrsYA88mhtBfHB09LrSzhjP/oOgiGX61WOxAmEsV1Eo768YpgW6ZIiCLXvjnf/7n CCERbeQHEXAatBERwGVCW7fbBYCVFaN5bRRtRBrAcWgjYgEnGjeYg+wKcIv2DQDqjcYP33/ucIWW KLc0cZUZ9A8UfUwXlImDeXkCCZ8zNQ4ps1e62TVTplpJY1Jg6AaLgEtFG9E33xyYl5mgjcgEcBzd iETGSdFGdXnZkxo3WLRp9XpDRreVH77XnYvqREFNPmA/0A9QDEsepokrHW1sYfSNwxbYx6hCznR1 pmSwFFUs2kxez+nw8KjVWgGAXi/dvrFoA4DRaABaxknRBgDnFx0OcPqm29hYkxo3TpPJuC4MNUzG o7v3PijavgU1+YAtVdGAy1RyP0uUTBRgZpAdYlYdlMNa42K9m8MeadK+UuMmRRuxbybG7fDwiC28 HnAc2lhJAadCGxUFnN64AcDBwQHGGANfHcm7mDScZ/tGZBFiQMHrDQpyTDkzg2WZuCpmBqVlLsS7 FdEjTZ6xJkaMCOPprVv7WdEGAGtrTRXgNGgDgNFowAEuFW0wd3CpaAM6BkqWkgqM4zSZjIHNxPmy b0SGdslnTOTcMeUvfCkmTlPsVN/kWWz7qLKZjulWaI80uStqtYVBA1VM+v77PyVoOztTnogsoo1I Cjg92ohYwJmgjejp0ycY41ZrXfOag4MD9p8IJCZO1GQyXpwQZ/psyC/9rPSycj1OotTAp+9rZDKL LZzAmQghRM7PlhbbDd38hA+g7X+//vWvzb0b1c7OthRwKrQRcYAzQRsRScNNMswVmV22fr+rAZxY VEPANVfXAGA4mI263n/w84PvPvMTn0rvk9Jz2HloUlxmEAoGStaS+2SuSlyZpeXJRTdvUGOlAhxF G8aY/Jxq3IikgEutDgWcOdqIOp1zAFjf0K2aoDpiVs6rAMcZNyoEuD8YNJvpg7bN1bXhoDcZj+qN FZNSORS9mqVzjS0SWCUHCy18QUCxLnm5Jk60mdLyWNKt3L6oAhwNS8koj/S9HNqIOMAdHqavogeA tbXmy5cv6/WmabnnaAOA7uV5KuCOXvH+sd/vAgDHOP0lGA6NABfv3GyfHYFf+0ZEg4sQ0EZlSJPq JgedlNy/idMXmytPNrqVYtak4gBnF5ayooDTx6SsXr58iTFW7fMhiqKNqHupM3Ei2qhYE6cybqxM ANfvd+OdmwBwealMRDqXGFwEks0h0tOk3ORgHqA4zwyCr2uXKTOYYTZvOIEDK9qmZH0CO6RQq9U2 Nze510uNG6svv3ySCW30n6mA49DGSgScBm1UrdY6mQWiekF/wA9czBjHvGVt/eqrCTFXms0oqv3h k78uem2WtDuFBjii0JKD0lKZv6ugBfCFmji7Nk/xbuGYNalYB8ehDQAuLi5EwGn0+MmTqIanZruI c60xHg9AzTgN2kAWpZo0db/fHY+H9XqGTJlhlJrTBetlElmEBjhql+g/Q7gXspq4oqFc0JSRPMVW 0o1+KOltIVxOqdrttiYsZQGXatyIanVIBdzLly+lv5dGqXq0EbFR6pH2FAWqw8NDAJhMRpkA12y2 gBkk7XXPWftGNDUEfEYZdtMQxuNEtdvtMJODYGDiKp0czOM0ebqJZo1sbRra45SVnlkEcKloe/zk KiYlk8BU9zgXk3IyT8OJ6l6e93oDw0tJyzCZjACAY5wYlrIig6TSP42Gw5VmhnESE1lMOICQolS2 /GGSV1Wq1zw5WGc/CxSPpjDjBVZiWMrq4uJC/3YWbVefIzNxerQRsYAzMW5Up6enGOPVVd3EXSJi 3FhlNnFqwAHAH/3i105Sb3n6aAi9Tix/aOQlUk0eLHSPSYsimcghkeuZ4oWgrijVeDwWNwVh1Wg0 Pv30kw8//EXWjBIHOBO0zYs0AIBeT3nEslTkwweDLgDoGScthgXgVH/Kn3qr6IQDquv8oNsimScH HRK5bv5ZYV5RTqJxI8IYqwAnNW5XHzgHnDnaiNrtNsbQbCoJwun0dGGHj8GgqwKcaNyoSJQ6nijP xBK1vrHZvbyAxYkmeVJv1Z1wQFTp/GBQaCMyHG0oIoiuZ71jgwIcfVKNx+NGo6F/sQZwehHAWaAN AIbDngngSEzK/VIKuMPDw9SSIMVk3K0t+Qzn9Y1NACCMs1ahw+t+Ol6l84Pi5MGgyJs6ebCIILr2 4MGDTG9otVoDbcbaj+I4brVanU6n3++/8847dLIb2XOcFUe9V68Ob9++TQGnN25UT548bhgHfRRt RNPpeDodL+z9vSgp2ogmk/FkMmbjTfbAecVbElAsiCcDpqy2tmP62pWVJinGdDqt1et39t99dfiN /ruI2GtRXN8YDAZxHBf0+WwV7ApW7n1B6MAWfjAYFNpidhLbirZ8EU4z80qscodQNQZhPB6vrhod 8E4dnCHavv76Mca4178EgLXWhsFXSH6pMXGpZaAmThOTipqdO2/0qpk2trYB4PK8A2apN89DckUE g67yg1CSiatcfpANVKHgCNpmnWkpTaa6in/yJ39C78Ner7e2tkAQabhKAWdRjF7/Ug84TbNIAcel 21QaDLqdzmVqP5gIGTdVlKrRxtb2anPt/FxXsBKnGoAjjjivguf7Yjnyg4X2H8tV9N4uZCrjOYsh Ak4qjPHf/u3/hTF+8MZD/SuJcVv4CjXguJhUFAc4TUwqe28fjE+iYUUaaFORdFNpa2v3g9PTz3d2 uPKFsA4pZ/crrgp+UHKdHzSU/Q5IRQPOuguaAw4Avvv2hQZwItpmXyGLUlPRRjQc9gCg2VzLhLaj o2Pyg7jHr6EaK83xKNsMlS92dx+dnHyxu0tKGQLXqOw44qEKRaPEOgEfgokTC19oc+Xa360gwOXv ggRwmlFUNh7UA073LYyJM0Qb1cuXBysrfI5fpaOjY7YpVCfRiGEpp8bKCgCYMw4TwJ2evnz7bQiG a1SZbozrFCEEnx90Tt68e/M6BJyfRCMR9/lSwKmMGwDcuX2n38cA0Dl/tdZaB8VIgr4A1MRlLS1R JhO3s3eL/txYWTEH3HYcv4zj/WfPEEB7d9fwXT5l0gNLmbUfbIrQs4kzzw+Ca/I62Hk8P+CKeK72 ej3V3rzSRD4HOD3aEJol67e3ZtQ4ax9ijHd3jHDDFiB1QhyNSUWxgNMbNy47aWji4nin3T7DGBOu PTo9pVFqUNL0wNKj6TBThN5MXNbnilvyujlXwW7hjluzliQLt/d4PD4+Pr5x44b4StV3UcCp0Dbn GgDA2lrU611940/ee+vrx9+cng0AQM84Md2mARwXk4pKPS9Voxs398nbVaLXhZSARKkhA469K0rn mqZsJvKTIizOxFmX3yF53dAt6yQ4h1eu1+ttbSm38BYBp5+B8d23L0CNPw7iNcXi1tOzwXA43L8j cY6qkQRVlGrYPpOp7mVsWMoKITTbEGmomsKKuX8EDjgIdUfCrHest1C6IBOXv/xOyMvP7M9ZmtSX xXG8s7PT6XTOzs6cXLx//Md/1H/O8fHx8fFVcJf6pUdHR5cX8t23yfp2AKjVarVaba21ALtHj96r 1Wbt2Ww2X/4o+RD9txPGMSVRxqQSIQQKB53qrMU1DET/8r/6r7n3UsD5Oxwwi8JEG5XJPUJvEJ/l JwUjG9jllMPyt9ttYpusC+byPNPU9AcU0O0wxr1eb309ZdcgYuJMps6S4l1edDY25Wk7dqH+Wgv1 +rPqIITef//dr756gjFOEkwAxzo4k2+nUWpqTEoV1Zjt2BDKPMABAADNZmtjY6u/uCeSFIsUcIDx F3t7gSDE5yyqPNJYknJD6fwmLsAUoePTmkXA+blmvV4vdRnW8fFxqos5OrraF5cDXBxvAcBo1G+p 1ypQwM1Wu2J4+bKzv78NWSbuEgdng7Z5IQCuBnFVYWkc74lvJHuEkJO3NCKAgzDGGaQdLKj5q5zE slU9RQjFh9J2BXNMN7Yc9J/+r9l4PJb+vtvt6l0eV1QSohLGtdvnO/EWAPT7lyzgWPsGi36ntbba 7w0g45oEALi47EU1lGizaSmamzg10JWgb7XWV1vr/a5uuX4I4wypUCh6wnkesbdJCFyjskgR+kGz xRPLWd6Nir1mrpJrJkrd3aHX64H2cGXWuLFSpeGouATc+++/S5nSWlv98cdsz7SLy1l4GNVQVNOZ TYlxY6XOxJmotb6xtr6pgSCUl4Yzz94apoNLkZ+1lnYyycQ5z6G7KhiVS+8m5j48Pzb7/X6rlb4A QOXgNFeIi1I5+8aJTcABQJLkuvB5TNzejTsIoSSZ2r39r/6P/wHjlPUPngdSLZyC58mrJir9TjGR 3iuVMkGaiB0c119TB3RTGez8u1dbyBpwKuNGRRzc3m48/6IFwG2s1/qLU8ciFE3xFADW1lu9br/Z NEIMNW4LH1VDAMAxLsW4AcD8EkRRDQBYxolJN5bdq/MdelPRNnuZl3GGPBFQOGk4VZYwNP5SBTuR 0OSa5opMWWsqulMympvn8w01nS6Aw3ADQi5ENblUlxedZ8+eca+MoohsmdlaHNX46aP3alEtQhEA rK23hsNar5ey+a0UbVdfxESpJmjbu3FnsZzsnuyOHzwEcF/s7RURpbqKgEqPUonfkdaCzn4opWB6 sWXTVKEU6QNVG7qRjzPpcH76kzjlTQQcSbpxooBLNW5EZ2dnnc5VDq7fv+S2AmYBhxD66aP3EEIR igjjAEADOD3aiFIzcaxE7xxFtUXG6VSvNzJBEBeThnN7Ozmc2JVJhrPAyiqeiRBCOzs7CKFAuEal eTBko5verKm+u2jASYth7uC63a7hBSMvY+1bt6s70I8CDgA2NtZrtRgAer2LVBOnF4rS90DnjBur nb3bkfYIsTxyCLiC5rV6dklZjWeAJo5UgRQstLJRSR8Mpr08Z9bDQ8pDfKoY5uDOz89BfZgWFc07 kNer1FoFNgHHGqiNjfXLS5hO2wDQ612srW3SP5kYN1Y4ZbZH+p+iej2ZzA6+Us1btlP+cQZvSywL 7ZPh3zImEkcPgs0Sipm4lFNj4jhutVrWB2pQFX2ARRzHTeYEdbqifjKZkF3eVDPgAGA4HAIAxlg8 cYaKe/D2ej3i0gFgPB6urCxkwRp1mDAH5t24sXtyPFulsLKyMhzOjNt4PGo0mpARbRgvbFonpRgZ KpW+Pd65QaeJoChCUYSTZKW5kDKsN1YA4G/+8n80HFWQ6rjVenR6emzwdFkoXo7TW7Kq6GNonNwy JR5GE6vPcwnzSBoitt2UdItdH3RUaHMcHR3dv3+f/pPdL2QymWjQxhoxDeC4bjocDs0BhxBiAbe6 usUCbjweYWyaH+DQRsWyTIM2AFhd46fCoChqrW3QEVU6YPrN098blkolAribvd6xwVbJPrlGVQRB NFDIqhIhYkLn0vmr0mAwIJ6Mj0wLnT9dnN/GGF9cXGxubkr/OhgMTI7LgvnwKxelmjjwbvd8fV25 VQlJwH35xWxjpai2nUxnoxPTJALAJvl7FdoAAGNMiaZBW7wj2Q+KiPi1yTjb1uR6GS7Yqvr6Sqri Flr6jAQz1SKcqTasSHkwxnX2Vx46WXGT4DDG5+fnqt2QzAEH5EDPOeBUyeDz8/Nnz569/fbbqhqt r9X6zAotQPDBB+9/8cXXSZJsbmxcXEIy7UwT6trw7EW2IoW8cXNf9yJZUdkjnOuNlXpjZTIezQdM 8/YE/YKtQGZOQah7TFL5gYh1LcLJxHFViMDviorixlw+//xzAjjVC0T/rHkxnUOnaZDz8/Pnz59L x0/JxIvW4gothNCjRz+JogghtLW5GdXEXL7yuzTGbfFlFpePRx5hXPbPUUocSC1lEY9e1rMxvE0B K3T6Qc5ahDDUK1ah9uGHH5aS7CgiVr916xYANJtNbp/eyTwHNplMJpNJfT4fgownqIQxZme3SbWy stLpdGg4Px4PV1fXELrKozUaiB1hQAgdHZ2Qn5vNZr+vmhqyuKuaGdp2926TgWM2UKVixxOotrZi 9rto0u0v//f/Ps+QglQkDTfZ3/efXzNU1jyXwxSbocJPFJaSiVNVISrl4VnEU4giRj9jA+YmLvVl AJAkib5xxA+5uODNeYvf5PInFD17u3cVH2xzRViiJUnCIV6xol7yy3q9UUSX2I7jl2+/vf/s2d2n T+02ofMjk85Zovd065KcG0//Jk5ThZQZIcXJoYOj5D46Orpx4wZCqN/vsxNEJqx9mv+GW78lis1x aNKF9IFPXzMaDbjdblkHhxC6cWPveD6EutbaVDs4AECZjBv3S2ripMYNFnflpcYtimqPv/pd/qQb 1dV46GBw3GodWU0W8SmNByllbFdU/tunUOPpx8SlVqE0uoHTK0S7GglOa7UaCziRbgDQ7/f1M3jF gqkYt7q62ul0tre36QvEvby5+JQF3GSMk0QxWImNlkNJ0Tb7AIwxxq11yY4mXFhKc20Ow1IVC+xm w/mUGKUGwjUqa4L4qUjR01lM5qy4398tk6xDVJPQIDX2HKmPvJMOTskHTy8uv/3uAGP84sUL+oLU +JSMMBAkbW1tNeqy/ZRwAwAQYJRmo/Qj0fHuTcI48X3iix2GpZqQoayN4bKK9k9vQweZZBEGeq5I QStnxRUUUpXp3YgyPXxS106Q8xMQQiTrNBwOm82m6N0uL2cbz06nU3b+B5WmSCxKzi9mn9M5P9/e 2jKPT2HRwTWbTZmDuyoVmqFIQgONcQOAePcm99c5UgscTzCMesJ3cMnFsfQAACAASURBVABA+1tQ XGNlaOL8j4EQuTVxmWpRPt0Ma26+doIArlarUcD1+/2VlYUpDpxr4wBnMqsIIUTRRtQ5P8c4yQq4 m7duHB+dgDiEKku3iYDTow0AWmsSV0jP+qOiYenTr/8pT8Yta9STaT2DZ5G6tNvtfr8f5qR8Kj1B QoipnWTisq5vKzkyJdLHp1nHp6SvoWZNJco7wwmTqpJ8++237D/FEHVjo44idPUfQh/87H0yCe5q CFU9ksAFqqkxqfT3m5vbbKxKjVu9bjSCIf8uq2HEQjeGs5ZYlxDmc6VKeh+FE1PnacPYasOY8r0b kfTJY/3MIfaNTokgw6Oj0Yg6OGnGjTg4w2fLZAKN+spksrCCtXN+HsfbZAYc/SXr4IhDXGlE4/EC oW7cnIWoa2tbvV56AYiJszNuIAx6NPKNJzixBuFEqRqDEOzScSrWIpUViuplYeKyWjaqUOgGTNdJ Ta6ZqF6v0zFTOvmDAk41ntDt9hGKUu9wGl026isc49qdjgg4QhM2+BUBR2f5rq2t93opZ+4BQBzv Ak6QejdKMeNGxGXcKAHrjZXxcBTHdzqdw9Rvn5fBZchTOuBMcBDs0nEqGqWGM7zLyTwTlxPQ6KOP PrIqYSEiYyuu1uvt7++TdfX6NQmsBoMZ9ZJEMomESDa9BPqDBR69+fANAHj48CH9TRxLVrB3e1dz 7jDGX3z+Na348fErTTnjeJclVFTnF04Zog1YfzdpP378hESsL55/or8EBa2sRCWdkWpRndCWjlPR upApSqWv/dRIvzrVcGBUo1C8G5fBdfJgvLy8rNVqq6ur9Xqdnbs7Go9VM90mk9nLSFZMauISmbHj TNx2vI0AWPs2HPZXV/nEOevg2PgUtA6OQxsA4GSKkynr4wxjUhZtAHh3d2dvb/fsrL29fVvl4IpO UXt2cNbVCTBK5eoS8i5sRKoSuoqpy/du0semqwfj/v7+dDqN45i1b6P5dm8rjYUkOjVurDgTJzVu rKiJY+0btVHb28IJ8BkdnIg2VsTEZTVu9cbKpH/FMozxkyfPSNaSNXEF+TVRxMEVesIWUX53EI6D 09QlnEKqxJq4/BeFqjS6xdqN5HZ2dlw5arJ6gd2CfMRsZskCTko3YACXijaq/qAbRc03HtwGgIcP H7KsEQHH0g3SACce1sdpe/dmvSE5MUsXk44PATCgq3cRwNEolcQ4PrcqIgUtLkp1SGp9eOVBJnUp vZAmcv74LIFuhnVw+MC5detWrdZYWZltDTJa3KqXAE6FNqrRyJhtAIAaw2EfAAjg3nzzTfaP1oDT GzcA2N65SdeTcrsYbW0tzBdnBxMmPWYWy5xx5NufPHmGAD179k+ljLsVkYYryIGW5Y8yOZ2QTRyp CEnmuKKwV7pl7Vhu41MKuJGwEflKo5FKNxQ1hgOz1Ay68oOjUf/BfTeAy4Q2KsK4dOO2UP4FE/fN sxckSi2FcQ4BV3Rk7ZkddtUJ0MSJFXHVkp7oZt2xXNWT2DcAWFmpi3QDgChamYyVQ6sougJWCuMQ PyHWCeDa7fbGhnxf9dmnydBGtLm921icpkvR9sOLvyeff+/emwv4YwCHAJ4+fU6i1OoCzmE2R/8t HtiRH9PhmDjVdXHSkoWPmeYcYnM16NPtdtfWWlFUm04T0QBF0QoARLU6PTyFFYs2AKjXG1NVBk5A GwDUao2N9SYIU8nFUVQyhIrmq0oRQjfno6irq6vn5+fc2TSsVhWDpADQXG0lc9WiGhuTtk+fE+N2 cdFZ9HdTgCmgOgBECO3sxGdnHQDY2dlvt39UfVFxyrNgy+ekVg/DlE4msgUylqp55DhpyaK8m37Q wOLTHIWo98gPUa3BLlmPmPOPRQfH0Y2KN3EytFHdvrUFwggDLDo4smnw+flCATDGn6c5OK1x2+Fj 0vm6q6/++W92Ynb3c8Q7OIAoWqXFAICnT5+XFaVajDN4G+SVfrVzc+S8OiVGqeZ1yVNI93QLPGtL ABfVGgBAARcJp7tTxqnQRnQFOC3aiAjgVPFpnTkfXgM4OiPv6hPs0PbZ39DZfLu7dMABwWKUurra HI2uPgFjXIkotUSusWVwCLjiImv/UapFXewK6ZJuVcna7u/fI3SDOeBEugHAZDzUo41oOOiboI1I BTgWbUQawA0uL+urswkudmj7j3/1PyXJ9M7dtyKY8Z0BHFATt7o6C4SrBTg/KTYTOTFHHkjtzcTl qYtFId3Qzc+j0uEkOBZwUVRXrbtCUUMz1DB/UQNShxoAAABDDQDu3FoHAXB7e7fF1+sBBwC7+2+o VtGnoo1esv392cYkIuC4MxIp49goNSjAhWDZRFk/lT1Xp2gT5+Spk6mQuejmNrlm+I2uLsC9+2+S H6KoDrKFpdS46QC36No0jMPMVpROAFdvzOxbs8Un2vVo29ra5O6Zu3fv0VdSxpH5z3fvLhSSM3Hf fPMiIdPiHv/OfxqOXc8QJteoLPptKQ60IMC5vTrmhbSk23LkazWAE2NSCeNkAakUcCzaiNwCDhjG adD2ye/+tyRJpFeNi1LZpR0awCEET5+9IFFqKYADgO2wuUZlHliVS2rnUWoRmDYsJPrNb36TCRYh PCQdAu7uvbcQwoRuREkyUaXbFgCnzbWxjBPRRuQccACwd/seF6uyaDs9PVVfNQSAiYlDCO7eXTiK kAMczBkXRQhjXBbgaFd8dHLif1sRO+m7bgg3Fy1J/lus9EQ8+u1vf2vI6XCaHlwCDt299ya3ZQjW LgmYTEz3dxwO+iq0wZxu4A5w64xra7XWQMi1mZT53r1ZlJoKuMkkooUBgKfPXiRJ4g1wrCMoa98k O6m6bjiDIUQ5b7EQpk+jjz76yOR5Ah6Ta4ZyBbidnZ3W2k6tNt+EHc14JN/DEtUAYCJb7cCJcE0/ 2kABx82DswDcuhCQbmzO5rL9P//x3xuijej2/pv1aFZBDeNardWLi6s5g8TEkdlwhTJO+pStNOCC 8g2s7KJU/9VRoWCWd5P+OdhGp8r/eJlXEO49eBsARYtb3fKAQwt/VTFO9Gsaxt25tY4xRgjlBNz2 jXvsn6zRBgAkStWbuFZrNpwqAq64KFXfISsHOAKO0CybqEx3WVnVkYL4alSBrUP4XKOyG42SuVEE APcevBtF/Ek6GCcc16hEwGlCUY5xK/VZRLmzgwAgE+BoobnF9ts37rFcwzjJcwU1gKN0I6KMKwhw hh3S28ZwTuR2J+pCZWLiQoAGR4OFMdNgg1CNMk2CM7gA6N6DdwFQFM0pg2qgilLnIozTcI0VYRxF G5E54Gq1+unZwp69HODuvfkIELKybBJJo9TV1dV79x5ygTALOAB4+uwFYPj66//XyTo8cztQ9MZw TsT2w3AWtKdKU9RwHCgL4gXvVjp67WTSP7LUDgHAvTd+EgkHsqgYh3ENALjzsVSaJlEk27zcEHC1 Wh0AdIBD6PnzT7B0f3Qbodv7D1nAsVN87917k30pF6V+8823SYIB7Bln3SdDjlJFEAS4K5FK4r0W JjdIOdFvfvMb1q9V6EnCSv9UsWp9OeNYwBGocdIwbprwMS+HuVTAEbQRaQCHMX7x4lN3ve0qDbey snL//n32byrARVGEMX76dBalZgVc/nsmQMDpK1WVW49lcTiWTVQcx+iP//iPucJVpZU5FfNUWWAc xgtzIDRiGSdCjRNl3PbujQifAMCbb77JTVvb27vNoo3II+CALZUecABwcTEi6UsLwDn0AkEBzgQE Fbr1wrRsnORrFSrUyqxosV03PQKA+w8/4E+i0n74ZDJO5RrV9vZuNMdZhI9F+4Yxvn37vvhGn4BD CN2683BtdVYqlnEc4JrN5ulplxYJAJ4+fZEkSSrgnHuBEMYZMvXGStx6NEEP7nYJL0Jyujlcr+5Z RT5S0hmXYH4a8HisO41hW9hMnOBQBBz5ltIBBwC399+UAg7mjKOHZFPAwdzEkdlwUsYVd+FKHGew q1TgaTjuCRQyjpXrTEMutFS0J8VxXGTPILHqzzgqJWndl8WcCDVRdQZw7L0RAuBY8oqAo3QDGeDE KNVPjOM/Ss3pQwO8AVVXKsCiEulW0QdbaE5co3sxnjM23bn/MwBElznoGbfSXKvXU3aC29ykm+Xi /vk3CC0caE9kCDgAIIzzCbhmsymm4dgolQWc54MEvQHOFa+DugH1sA7Tb6bsERJU+4oK4GFyZcEI 6QCguZpyjjrHOIZoC1prbZwcfmoNOGBMXEGA49Jw1LWZA85zTtoD4NymDkOghjmsQ8NFCt2CTcCl tngZDY3Y/z98+79gd81dX5echyCeArO2tgkAX/7hrzHg9lkbAL/33nviIAOEAThYTMO9/fbbbCE5 xrGAQwg9ffY8SfBXX/5DKYArYpyhuBC7RGpkhXUIOKZK398tNB5X5kmyCKOUNNtccbyDEJydndHa IYTefffdKIqsAZck+IsvvsYYA8bPX3wKztP2CD18+LDRaADwNlMFOJJMJKvuSwEcODVxHlKH/jtz nkoFAg2j3SsDKatFcwdSckPR5yS/XM4WcLSdMC4ccO+88w4pWyrg2IFguq1IWSYuP+C8zWj1aYvy VyqEW890b95yvXGe1a8htHKqRHDnBxzXVIEArl6vHR1dsKWiq+4rB7hSZrQW3Z8dVqr0KNWUbqUk 4JZy7ImTpo55AEdecHxyyf7SA+Bu3X5ja3N2wBjLOAK4ev1q7RplXBUBV+5M/eL6cxE+tMS7L8O5 Cj5L6bz3BAg4kzraAY79kwZwGOBFAYy7feehFHD1ev3evQVPV1HAhbC40nl/LpTXZd192U6N8VDK glo5qMHfPEtzUgHH/RI0gAMowsSRbctu3LhB/kkBR85s1QAO5tuXBzuQGtTiSodxnwde+49SDz4+ yHwmVqGuuNwzJjzIemmOIeDu3Hkg/QQ/gGNrx6Xh2OOoVYCDUscZUgdSQ7BsovL3ap/18nMPHnx8 gBG+/x/uZ6ZbESbI2yOx9LER6zoaAi5J8N27b0g/wTngfv/732+tzhbWtvvwp3/6p9xNwgJOmAqX AjjAGGP8ZRhRalCWTZR1ry5rVKRQE3fw8cG9v7qHMAJsdZ6pQ0b4b99qzRviPkcKOJgfOpMwu8Xd vcsvbwCngPv973//v/67/7wWIQAgM93+4r/9uz/68BeclyTjDDtxC4Sjv1SAQ2hhPUO5gAuca1QW yCjXihZ0G87Qlsy327H4CLJSPWc54jje2dnpdDrszFUPclJ4Q7mtI1dyjPGTJ0+SJMEYv3jxgvv8 H354IX7Cjb0N9p8IoUePfvLBB+8jhAChNx9+CELOTiWMcS1Cq82V1eZKMp0k04m0ghjjwx9fnLZ7 APD8+fPnz5/TP33//UIJb97cvHlzk3w/Quiddx4ihBBCP/3pr8RMYqHCAF/s7j46Pd2JY//9007t dpscQGPyYtotS6yX89vw4OOD7/7iOxZtYEc3yFe4srhG5QFwBdVRCjjicZ49e2YHOMK4rICLoqjR aIzH47HB4YevfvyWAA4ANIBLkmR3d50WrETAbcfxy7ff3n/27NHxsdcvzieTjh0Hg2xSWjIGlVPE sj34ywcs2sCabmDFiNK5RlUc4IquowZwz58/twAczE0cAdzDh3+UumwsjuMkSUy4RvXqx28PDw/J zxzgCONoWM0B7t1334yiyCfgZvd/u/3F7u4Xe3uPTk+XA3AhWDZOmSynSlfRqFAtm7wblfkIQ5j5 C7fBv886ijm4t956izgdcddykxwczE5H/QpjDIABw4sXfwChv9A6Jknyh08/2VrF//O//S//m//u d6dd/Ecf/kI8LJHTzs7O7duzMyLYNFwURbdvL5zHKt35sugcnPQKBrV9uaHENFyYA75UdkMNV2Oj ifwBlItuYACIMLlG5HD813/vyQ84WGQcThKM8RdfPp7XAr94fgU48TqSH/7w6Sd/9OGHJMY1KbYU cASLJoADgCIYp++lVQQczHtIyDcgp0xugx0bVSkv3TRlqkSzOpkuVFY1HQKOnhAoAi6OdXtMkqlt mYrNAY51fKmAK2Ig1eTJVF3AhWzZRBnej9zYqEoO6CaWqRJco6rWdCGxDPkBd3R0zv6TA9y3L/7g vII7OzuN5uZuvAYApMD0Tz4Bl+kKVgtwtGpkA+RwFuqkSh+lpkajrNzQjYR4cQWPsifKCrgQuMYW Jg/gptMpLEIE6MblM8YthKgOdevOG7vxWq1Wg8UVqRzgFouHn1DAfZELcCa+5v6DdzB7F2EABBsb W9IXD4bDXq8PgI9ePS+iucwlVi2EhTqZJC2wSTTKyhndoIJQY2V4+YPiGpU14AjaiDjAwYKJKxBw N/dmuxbrAXd2tWldXsDpL+L9B+/E8WyaAmm68SgRXyZVfzAkZaRN1et75Z2malUEHGviDKNRVg5G FWhrVq75OOnLHybXqOwAx9INSgLczs7O3bt3yc8qwJHZJ5eXI1oqO8BJL+L9B+8gBNvbC0RjtdJI OSiD1WW3x/5zMBiQIrO8O3r1TREtmepGq3iHxnH82a8+M49GWdnTTdpRqth8rKTlD5xrVFkBx6GN SA841UyRnFIBDgBu377HTqzLAzj25r93/82dndkuJiLOtjb52HMwyDC5jwOcqMGgf3nRG41GFxff m3+sXoYDCEUv83Sug48Pfv67n7dP2xadznKdqepuD2qjIQtx5a8K16jMATedTjHmF3sSaQEHBZk4 DeB2d2+x/2QBBwBPyFQ4LeDY6/jhh/8Zmb/CHa0tEo2TBnCtFm/uzs8XphMOR0PxXZ32+Xg8BsAn Jy/ytKdFL62KC6HRqB2UM+/vltqOVWk4lSo3S4iTIeAmk9np0eLpfETiOEOAgIO5iVMBTso18qcE o1SisVpbW+/3JZBSiQOcVKenZ5PJBACPxxM7K2c95yPw+1Q6Npq1zBnOVTC/2wNvOL2Wb4REBBxF G9HSAO7w8NXpyVVKi9z5d+893Nvdg8UIdHs7BoDpNL38a2vr7D8zAQ4MGHd6OvMjFHMnJ89NGjb/ AzjYKFUzNpqJLUYn/lk0YhUBt8QjJCzgptOpuO2lOeBgPlMEJ8m3334WFOBoGo5M8lrf2N7d2ROh xkoKOI5onAwB12jMtl/vdC70rzw+OUmYHOhkMgYAPeYcTtMNraunjo2aQ1lHtzwPh2ol4F6HERJy k7/11ltJkiDZ8c+GgIPZilSyMRwuCHCNlY2bN7YQQm+8sbAZZwrgnrzAgDHGjfoEIcxuByByjYil mx5qrKSAozgTlQo4ADg+OQEADnOj0eT09DnbwkXkTMLp6ubTPkzKLKebkxYMp8n00jwGq1IFldjy 02tKDFFOwE2nyZd0IBXAOeMI4G7d3AaAVMDRS4cxfvHiCVcSFdeoms01ixJOJtnqaw44KkK6Xn9A w+3iVlaV3tUzLUIgSjVxPN3cPhlKbzK9XpNBEnEBCVm+bg24JMEAgDH+kl2wVSTgYJFxLODG4/Fg MPveZ8/+GWNcq808VCrXNjY25h9iOl+32VylP3e7fcN3EakAh5iVtsfHJwCA8UJ5ut0eThKM24Wm g0tMw2VdhMBKc4de0a2gUcIw6fCaDJJo1sZpAAcKxlHAEbqBF8ABwL17s2m9IuDoVLjPP/8M5mnB Wm3FnGtUqYBjuUaVCXBRrdZun6e+jACOE8ZJ97J7woyZFCT/vd1iEQInVZnRRx99VOjsh9AScBaV Da0KhqJRjKr8FoADgONj/nAGAKBRakFpOCngNjdnqws+//wzejXjOI4iZf4LZFyjkgJOCjVOGsZF tRr3G2vAAUD3spvg6anZoKq1vAHOIhpVSWo80W9/+9uiZz8E4n3yQDyQKhhKrKmq/FkBNxyNAeDi nM+pMybOH+AwxgcHP8CcsMB4VYQa4odouEZFAWcCNVYc4ESocUpnHIbj42PZF/UwJCfHxZo4D1Fq nmhUJX4kzckq+qzf6lmvzyCJxSLqTIAjdIM0wEEx4wws4AjaRK5RsYAz4RqV3vpp1O32U6HGSgI4 ocHkgOv1ADDG2APjCurz+aNRlVgue6IblESH12eQJM8ICQUczA8PZP9KAUfRRqQFHBSUhqOAY48E EdFGhFAjE9eazdmCqvFYsgJXIwq1rOMM7fZ5agtxgKNJz8FgAICPjp5WC3AOo1GNSLH90Q380uF1 GyQxnCigBxyoB1I5tBF5Btx8kOEB/Y2KawCwsbGFkKmTolyjMgScaNbMAUcaqWOUhjumUGM1GA4A 8NGrygCuiGhUpTiOvdLNT3q+6CWiQQHO7QpqTZR64ya/4RoVx7gixhloNT/44Oe0YHq0kR9SASdy jUoPOE0Qmgo47lrpAUe3cjk5ORV/P56MEcCrV/wsP7dykoYrLhpVySvdoGA0eFv6HgLgChokkQJu NBqRef937z2UngeoT8MlODn47nPr248605/97GeUViZoI9IAToM2IingTJJrKsBprpWUcdwuVSzg 6J/IAtWiAQc5ur2faFSUb7pBMWjwv6VHiYArepCEA9xoNGL+iCwAl+AEAFsAjq3pz372M4QQQGTO tatCC4BL5RoVC7hMgwawyDiTa8UCTrr7HswBx/6V/IxxEibgfEajnEqgGzhFQ1lbFZU1Cc7P8mkW cMKpzJkBN03IrZgBcNxlnaMN4ng3K9pmhZ4DzpxrVOPxNCvXqLrdfqZr1Wmfq7hG9erVEftP8nqE UJJMMcBRSFGq/2iUVTl0c4WG4pbdGX67T/vmefk0Adx0OuWWeQJAVsB9/sVXdJzBBHDcZaVo29zc BIB6XTJjQ482otXVDOOnVLVaHQBG40nqK0WRGmQaSE2mSbvd0byAPmzOzmYXjtIQIZQkCQZcNODA oPOXFY2yKodukBsNgewu6QdwhVZWU4X79+/Tn7nNiDSAA4Fx5Pb78qur2XAqxok15dAGAt1MuAYA q2T7XJzNghG0EWUCHHehTACXTBdWSkgZx/loAjiWbuT/02RSLuBKjEZZlUY3sEVDIFyjWoJxkkIB lzDnQM8BB6KJk9ZURBsRBVw2tM3KYQQ4lmusTBgnvVZ6wHFoI+IAJ6QIAADOztoi3QDBdFoa4MqN RlmVSTfIiIbQuEZVEOB8xt36KlDGiYCDtIFUSjdQA05aUxXa5gXeU4GV06pw6EEq4FRoI9IATn+t VICToo2IBZyUboPh8PJitvKXyUgiQJBMJ37GGWgaLoRolFXJdDNMwAXLNSq3gCulvraAA30ajqUb CIDrXv4Asi1M9GhbX19vGJzCJ+HaVTnkgNNzjUoKOMNrxTFOgzYiAjgp2gBgMJx55MuLS45ugAHj 6eHh46IBBwBxHH/2q89CiEZZ1R48eJD+qsI0GAziOJ6f+ShRHMetVqvT6fT72da4eFZqRQxVYn31 VTg/P9/a2gKATqcTx/z+QhcXnc2tbQng8JTb5REhdGNv9+aNPTKzYWVla2Vl67yzMAiYijYASJJJ rSZZKk+lQxsAIMyu5SIyRBsA1GrRdMGTGr4PAGBlpTEeTwAgmSYmT6/V1dWLC+XhDJN5WLrSXBkN R1fBKRDERZeXkpWqzvXkT578/Hc/H/ayHTpRtEr2bkRS1xC+XxOVx8EFUl+3Dm44O5sdBgPeoaii VBO0UakcXArargoxc3DmXGNFHJzd5bo457c7Vomm1ej5MlTUuFF1L+nHotmlwMmPP35dqH3LeS5f cQqCbqDYI7tCXCOym+kSWn0NAQdp4wwUbUQmgPvFLz4kN7MUbSDQDWSAM0XbrBA1O7QBAMZ4lHG9 PRGJRk0GUrm5bxzgRLr1e32Y5Tpn9q3Q+NTJuXzFqeTIlKrVapHIqBJxqEoW8SlJqAdVX8MQFWRR 6sVF5+Kivbm1PRyMuDfW60gapc5XF6Gt7Zsb6/UkSczRBgBsfLraatUbunBVVKNRx9gmBT7fBzia yta3a0QTbTRETf0Wqlar1e9fXZeJMO93Mp4AAEIIY1x0fEos2/aTbbTYgK6yNPkVindbglNEqQyf XaFZNk6ptdBHqXt7+9J3SR0czGfDkTv/4cNt8Y1StBER+5bNsgE06lccTBI+B6eXeMkMTZx0DEFl 4jSLFk5Pz0TjBnPvNvsugl1ETJvj+NThuXzFqXy6sTd5OJ42p/QVCZxrVNaAGw6HAOjuXeVsOE2U KgWcBm1Em1u7+hdwYtFGZAg4zSXTA04/NioCLnU91g8vfxR/ydJtmiSIiU9dAS7TtI9y7+gyI1Mx Dg3H0+aUqiLVCr1TL4d0IHU49xQXF53NTdlAqjZKxQkGgE5nGMezvb9T0dZqrSXJJDJOn4loAwCE cGqIqn8aaaLU1GkfXJSairbhaLS21hJ70YT5EFpahAjlUP74VBWNqlTuHV3mSqxlPUWUiqtIuati rWXu4ADgzp07i39EAKAycVIH9/nndFEqPHy4bYI2+nO9kX4YghRtVBoHZ3jhRAeXijYq4uBS0QYA w/nGLeyeSKxxAwB2zgpCKL99s16EUNYdXc4OSPq4rKJnUElFrmtVQlGVTAA3nd/D9+7dFf6ujFJF wE0mUwD46qvHGONHj/ZVO4IQsWgj0gNOjzYiKeAyXTgWcOZoI6rV6nR5vErDET9iQxinoRsRAmQH uPyLEEpJw/needzwJl8a+7Y0oyUmV2R/f8a1TICDRcYRSmKMv/rq8U9/uq+Bm4g2IhXgTNBGxALO 7sIRwGVFG8xn3ukBJ9INAE5OTlPpBhgjFGWdIOJwSbzn+zrbUJG14jje2dnpdDpnZ2cm3YX4HQ8F K06kyuRaVh1tYHZFBoPZ3fX99z8If8Q//PBCdUetrvL9ECH0/vvvffnlS1XLqdAGAJMxn+Vp1Bvm aAOAKLpa+W/+LlYrjZoFDOjMu50dZVNL0QYAe3v8uEotEu5uhDBOEMpw119Foy66sOf7unDvVtAG 2YGLS7FVtyKcVBVhrzJ1cCAxcSlpuClvdi6k3k2DNirq4DJxwarLSwAAIABJREFUjdV0ar8UnA4R TKem3V6cVCx1cCq6AUD7rAMAl5dXy7ak9g3MNvItbkm8tyi1wDHT/OODZIqv21IVLVprluZLPBYs XuXLyws6Hffi4mJri5+aqxpLrdcRdxp8rTYRH4omaAMAMopqjTaAKIqQnXVjRz8NP0S6XqLVWm21 VrluoxlzGAyGALCyskI3i9dYim73VPUnyD42mkmDwcDPHVEI3VzNe6gWFPS1rlZdNGIrolpocXl5 QRmXCXAAk1oNJ3OzEEVjYbK+EdqILDYZnyuaFyAz4MTlB6kfol8KxgJOY9xgTjdgACeh29wMr6/v drty9+RngzYPd4TjyNTz7tiB6DUcLTGscqZxBvZ4mvE4AoBarQvMDnGZ0La5OVsuxk2sM5AkLWUY YGpWVmk+wWShK4lSdWFpuyMGmp1z2VmCs+AUv3rFjy3436Ct0DvCGd3K2h27dGWdxRZyXUxEL3Qc xyapEz3ggEnDLR6+BeNxROgGc8CZ042ijSgL4JQZ91TApS4alX6I+Rr+Hw9faf5Kkm6cTs9OJZ8/ pxt3TmBZ24UXl4ZzMGaadTzUQvpJT2WJVjxTrSs9HExQTi702dmZSUVevpyNn37//Q/CWCqmY6kj wZU0Ggs5OGu0AUC9bth/dLdDrab7EBO0iR+SaXuS27du3r510/z1RNOpUDCEYHZPXRXG7dhoJrXb bcO+lFW58m7e1hWFlrTKWfHXbbTEJA23tibZFwTj2Z3ZaDQicX6DTCLaiKIIieOH3EtSP1yVQTNE G/chmdBGm31jfb3b7YkvGAxki+oHfQDAOME4kTZgt3t68PFB573O/b8qebvwIu5x+8jU/7qiEGI6 VwF4CHUxl/5am9dFFaUOBgMAdP/+W6ybSJLZk6Nen1HABAcquhGpQ9QMQQwXXWZCG6MMp3OJLX+4 eKSpNOkGAKdnCwOjVw3IBqdHjwE8bE5uJLdRqk1kaheR5VfpMR0bl+X8qNLrYiiTa21eFzZKpb+c P67xwcE3BwfPNPeZJMhalB5toAxRs90FbHRpizYYC1OOM4mPUs3642QyP5yBDU5DOgnBbZSaLTIt fYuLsmI6aVyWU6GF25wyXWvzurAharfbazR4O3Z+3t7aipNk9lHUuBGpIiwwQBuREKLaPOBJdJkD bWMASJJJkkxSDammy9EoVRqWwjwyZZUkSZIkUXTlHDVTQ8qSq1vD9NJ6GDowUUHZR40KrXiwDs7C pRrW5Ze//OX+/p13332nVqsBwJlkpA8fHHxDfuLQRiR1cIZom38sNV/2o2rj8Qgg8zJSEE63ym/i LN51ZeIADA9O9Cwnt0b61Q2Ea1TeiOCn4qEBLk/awaQuCKGNjQ2E0FtvvUmGwkXA7e/fkbyTEQe4 TGgjqtdRHrQxXikb4KQH92kAZ3IVJuPxxkaGmYCzd03Gk8mYXIJbt94NkHGkO5F9KCwUx7HuAofG NaqiieC54uEALn9i0bwuCKG33npIAUcZt78/2/VIatyoKOAs0AYAN2/eunv3lsUbQRIGmgJOdSYp AIzHg5wmLjPgEIIrExcc2ois03CkJ8s7UPj7kRU3A66UPSYJFEocRXV4xTV1+eUvf8meCIMQevTo ffaf9Gc92qis0Ua+6+7dWz/8oJsiK0qR4UpSnaAGbcxrBg1m+6as12JjY+3y8mqyyFmbz6bNT5NZ UHM1s+/zrEx3B9uT+UsSrF/jVEQCrqyxYKKyZiwXccVVDo6r4+rqKmKU9Vs2NlK27ZWKoo0ok4NT Je8BwC4HJ4o6OMNr8cMPL9l/bmysURNn+Am97jkAvPeTj8KcM09kGKVywUfE/qESXKNyGNCFUHf/ AybgdI4LJ/Hq/PKXvzS8fwyNGwBgnO04UQ5tRIaA06KNKFExzsS4MS/OEKJKL5xFGi5ktBGlRqli 1BVBGPe2nfIDLqi6+0zAeTCqXHXIeAL95+qqfAddQ7Rtb88OzTIHnBRtRKmAM0AbFQ+4TGgj6py3 zy8k60Y5ccaNVVbA9fvdsru/kVT3iDShFIVzb9spDxGKcy7W8gM4bxUvqDoUbUQmgNOgjUgDuCxo I7oCnAXaqFIBp798Yc3TdSexU6ly5fUlOJ/FwlSHPGxS6AiD/4rT6rDGTSUyCc5CGE8R0r3XpJNI Bxmyo40oAYjs0NZjTps/v+hsbUrOrjYXAoxDHRK1Fj2Jif5T2p89natQqDJlrIIKRVUqwvKUWHFS ncnkapKaKiw1EWfcqDQO7uZN06GDu3cXZorYoo3IZpyBRRvR+UVHauI0YSknQxMX+MACJ/r41zyq l4FuYIaDSnCNyi3gSo/BTW4bYtz0JVShjUgKuNSYVJT1VDhW3W53NBqMRm5W2omA0zfUwcEB9xsJ 4ObNQoZNK4Q2mO/dcHZ2pukSS0I3SMNB6be3hZwArtxpLlQPHjygN0+qcctTVA5wFmgjunv31nCo 2+Zbr263S382B5xo3FiZjDNQSdtQ7+CqMrAAi4k2zWSRDDtMhS9pPw45xZaqPI/TACs+mUxUQ6Jc xg1jLNZdb9yY985ycNZoA4DDw6NWa6XXs4lMWbQRjUaDlZUUpuvRRkQAt7W5bR6WchoOBwDQbNpn BkqXOIZAolQxW7083g2EBFy1QlGprCfBVdGrcuJKboi2+XunOdFGvn1trZn1vSLaiFyFqABwfpHX iRPGiQo/9aZZSiTGOkvl3WBxMCUo22KtrEOoAVo2AFhfn60rkIalqqFS6uAyoQ0A3n//pwgh2QYk 6aJoI1pba9o5OFEEcFITZ2LcqI5eHWGMWy3lUg0x6UbUZ/YUmpm4lQV8h4w2k47N3SxL5d2Ilglt ROYJuPAtW575X1m1s5N5LgWHNqK1taahiVMZN1b5TRwpYb/f7fflX2d+9Zura2SdKRlYCFbmHZtN w0WB7E7hRKRWhNzB3t52SgVcIKMHJuIAp5/jhjG2M27k50yAk6KNKhVwJmgj4gCX1bix/1QBLpPo Qvp+v/vo5CQ0/5Z1Ywu6ZisK2Yuai0uxlbJms2hp1qAEnl788MMP83Szi4sL8xezaCMyB1xq62kA Z442Igq4TGgDWSFdAW59Y3N9Y/OL3b1Hp6cfBMM46z172u12VHUQqO7tcDZNcyjVGpRgucap2ZzR wTw+Jc4uE+BE7exspzLu8PBI/wIiKeCyoo3IYjYcZ9yo2ChVlXQzFYIvdne/2Nt7dHpaOuBybkcW QWVBkOpZKlovvagxqVAoKhUBnPnSKxPAicaNlQZw+piUk8VAqkqdznn3MkPCS19IArj8/QEDYIAv dnfLBVxOtAEdVahcfGroWSpXr1QRrx14KMqJDphmFcc+PeD0aCOSAi4T2ohYwNkZNwDodGZcMwSc yrix6ve7k4n9JGRWJQLO1ZN7RrcKxaeZal6hehmKznepBNf0StLOTxalApwJ2og4wFmgjYgMpOZH G1H3Mt3EmZTz8PAQAKSA62c/YqoUwDlMtlzNCAk/jrPzLOHXy1Bs9ZeG2poEnCpozZmDAwZw1mgj evnyZc7DEDhpAGdi3IAhoHMH52ecIX80ymphvluwcVzOWGwJACc+0CpXKTqkkF8c4MyNGxUBXE60 kbdbAI4zbqxUgLMo6mQycsI4AjgP4wxu0QYc3cJ0BE6carDgTpUmEq8K4PTnPUvtW+poAwWcBdqI jk+M3JBK7OXIBDgN2ojEKNXQuJGwlJMGcFtbpp3HwziDc7SBuFYhqBvG4bBgmODWy8SxBnW9Monl l90Chjwh6uMnTzDGacfAK/XyJb+IPf+RfZxYwBn2f9XL1CYuG6mKA1wRaAPpSqwQbE4Rw4LVAkHW pSd+SlWcWMCZTxMZZM+Uwxxts+/KDjgak4pKBVyqcWNFAGdo3FLl5K52DrhCJzZJ6FauzSl0ukMl QGBxvUN4IOmlD06Jsjq4RqMBAJ9++knOflKrZ2CcBm1EGsBlQhtR9/K83780eaU0LKWaTBIAQC4Y 5xBwRc9Fl6+iL4sCHmbeBw44uxaoYtytUtajFTDGmQDHGreF7zUAXCraiByGqKenpwAwGKTPO8nw LMzNOCcDqQVFo6yUe4R4tgM+Z96H6XRytkDg1AaAXu/qmHQVwsztGzFuROaAU6FtVqo0wJlfGjEN Z2HcTk9P6TcOBl0N4/TGTSongLMeSPWANtDQzZsd8D/zPjSn46oFwgdcqpIkYSFoLhPA6dFGpAGc OJKQKgo4C7SBDKYqwOnrRcJSUY2VlcbKikXBZl9qG6X6QRvo93cr+m4pcUVROCBwG4yHUy+pDMmV +jLWuFHpAWeCNiIp4AxjUlHj8cAObSQmFWUSpWZSfsZlApw3tEHq7pXFBXGlb25ROggKCsZLr5de +QEnRRtR1hycShzgrNFGNBz2hsPMhlTzjVyUahGWAsDO3sK5Xzdu7jebrWazZfFRhoDzv+9DCt2K COLC2dyixARcoXAPHHCqpBu37NRhiGpu3KjoQGpOtNFdsDMBTmXcWFHA2YWlXOfPeS+kjjOU4mbS dx53eKuEtrlFKQk4P3APGXDm2BJfqTFuVBzgLNBG9fTZ45xoY99tCDh2MEGvwaD73Xcv7MrmXJpx Bp/RKCujcxXye5zQuEblkwKeGyGcoeFPPuHNVB7AmYgCLg/aZgUwm24mikMbkUmUmqnAGGOHp21t bGY+iWKhMLIotSy0gSHdcnqc0lNsevkBnP9GCGdo2KTKmt2QKOBMjBv7pTlzcF9/PTNudoDTfLMG cCYxKdXR0TH5Ies2v1zSza1YwJWINjA/E8sOAeGk2PQq1OaU2AiBxKfSMmQ1ZZnQRnRycvLdt5aB G0UbUa9/mYlxqSc0qgCX1bix/xQBZ5h0i+M98sPf/af/xUkvxQAv33770elpp9Sj6TKc+JcJAcGG olIVZ3NK962lA460gPRPhcanNHv13bcvsjKOQ9tVMcwAJ41JRYlRqp1xY2VxVgMA0Lm9rnppHMft TsfnxnBSZaCbIQKqxTUq5xQIx7eWuK6OtsDlpZwLBFupm/SOx+PjY8nNrBHX7OaAU6GNKBVwhmij ooAzH0wg0rzYYSbOQjQa9bYxnErZTmvW3ycV5RqVKwoE2A7+AceZ1qdPn+ZvCnPASU2QdZTKSQO4 rGgjIoDL1D5S48ZqNBpMpvIPVCXdcg4pEHGJNg8bw2mU+Sx6VXxaegjmRE5Gh8NsB2+Ak5rWVPtm KBPAaUxQKuD0xo1KlYazvuYvXx5kmhBn2rsQAqFLFz1FXyxbWYDLTDcxPg0nBMuvPAm48NvBA+A0 cB+NlJvEZtq89/j4WM84fftrAGeINioOcKkjCSpRHBsCLtW4AUBUY/Z51+LM1ZCCfni0FMBlphvI jtQM0KpYywIBFWqHQp/berhjjDW76ZpsAMdKBTiTxLwUcFnRRkQBZxeTguA0UyfEHR0d2/QxmYmj fyP/s+66hs91/4CzoVsVj9TMpEyACzYUlaqg0WGTRnj27Jm+iaSA0+yJJALOPDHPAc4ObUS9/uXZ mSXaQMEUDeBMyrlg3FghtHdz37hoRsrU/1MXbLmVDd3oGH8l7mc7mXic8ENRqdzGp+aNQF4g2jf2 jTkdXKYLYTFTRKVe76LXsznkQeM0pSbOJCbVCyEURbUoyrZFqEoWk3V9DqRmo1tF72cLpXqcalk2 Tg5HhzM1AjFi5+e6HYFYwJlsZkkBl2myGNV3377IY9wAoH02S7dlBZyJ0+QAl8u4AezduHP1sqgW RbWdvdvkn59+8tdZG8F6HYK3gVRTuomhaDgLfQqSCgHLgficgLNrBMN5IRYO7uTkxO5yHB0dXZxb jgYAQPtsYS5+JsAZFpiaOCfGTfxNVK9H9XrW4d78S6w8AC6dbpoUW+nz4IsWV8ElyzZaXz5r30pe nySJ3r5ZqNu12dPx6Gh2Fv3lhXw1hV4c2ogMo9SsTnM47OU0bg7lavVo0YBLoVtqPw5nI4qCRBFQ 6VBUpayAc+hbU+NT8zMWyIy5rICjaCO6vOhkYpwUbUyRdIDLuiwBAC4ue1ENRTX7240NS4ninRvk h0/+v//TsDzOA5dCxxmUdDOsxtLHpwCAEFqCUFQl8+eTE76bM8swPmUnA2cCnLQWhoDTo21eMLmJ s0Mb/VkDOL1xk1xolG0uSEEP+OLGGSR0yxp/LXd8GscxmaW5lGiDLMuHnfCdTb2lxqdZE3BgDLij I+URyKmAM0EblQi4/G1oYeLyG7dC9zIqaJxhgW7WeaWljE9Z97rcFtVw+bCbvXEWP0QFOGrx9ICT ruJKBRwXk4rSAy5rO7CAsxjYZY0bK45xhRo3P9u0OQfcFd3y2M4lu/mllF9ui6oZIHYejHz55ZfT 6ZT+07ODS0UbkSoNR+d/ZBKJUi0GdlVoozIxcaJxyySfO1C6BVwEjh7OS3Pza+7npbSoVKoBYufd WvxAO8Dpl993u12RcYZoo+IAlykm5XR6epp1Qlwq2oiiGkKR7sg+sd+ah6U+0UbkcJwhcjjFoeo3 f+r9vGQWVZS3AeIvv/wy6ydzgDPcWYQDnEV1KOByoo2813pVQ6owxtLiyY2bQVha4rxOV+MMkcMe XOmb3/B+XhqLqpKfAWK9fVONq1qEqMAATjOSoNflRefs1P42EQdJTQBnaNwAAOMG8zPPODvjVvr8 JyfjDDbrTDWq4s2f9RlVxToaigwQn52dbW872MhQr88//9xwhIEVAVzWjci73W7WmJTV2dnZxUX7 4sJySYP0e/Umzg5t7DfqKptm3PxHoyrlBJxjukGl4tPrMWIqDvEeKogxJgOI7HeZAy6Tzs/PJ5NJ 1ncRsX3DAnD6QdKColQijPHOLr8NLzVuW1vyJ3Q4aCPKAzj3dKtKfHo9RkwlNoWfCkqPeTcZYRgM BoOB6dEB9APZgVpDid0jE+BMJu6KJi6ncaPa3buNEEqSZOHYivmz5P/+238vli00tBFZA8493SD4 2O16jJhK0xR+Kii1NoarUM0BRzWdTs0Zp3ryGUapmdYkUMC5QhssmuJkLnylhZN6At8bwm4gtfbg wYMiStNqtSw6X9GK47jVanU6Hbv8NKfBYBDHcYDVNBR5UGuawkMFLy8va7Vaq9Vif5kkyXA4bDYl 01O50k4mk3q9rvl8KSgxxlGU/lzXd5LRaNBsrqr+arHcajweDYYTulOugXR7tBHjxv0y3r1JfvnJ P/0lW7zUnhCIjlut47W1R6enx4sdRqVCvBsEGbsVMQxU0QSc+YO60g5O8wmpDu7s7Cz1q1UmzgJt ADBNyM1o9EaTmJT75QLamFg1zGhUqqwDqUXRDUKK3Ypz3QFCPFVZKV/0dXz58qXqTxyeVOZCCrhU OGqi1EyPQA5wdmhbFNYzLlNMKvml4NoqgTYqc8AVSDcIw9oUPXMnHIibyK43e6ijamzRzsGZ7x8n As6iq1DAWaNtbtxYyRmXirbd+Xa7rOLdm+QHNiatItqIDAFXVN6NqNzMFM2yFX39KpGAy9kahdbx 8vJyc3Oz3++TBBx3ND3JwaVmhdgc3HA4NP92Ng1n/RQcjQYnpye1mi4JqJIMbZzYuzhfum3ettVF G9Vxq/Xo9PRmr3e8tiZ9QbHeDUqyb/430Q3cwTkxsIVeShKfOnFwFhv/EgeXp33OLy4xxoNh5sS8 AdqAmjgnMWngw6PmSl2wVTjd/GemylpEEkIYLsphV/ZzKVWAUx1lz+n4+FhzLLT+eznPaK7zi6uy DYZ9c8aZoY0IA04xhqkx6fb29jJtnQ9p4wyF0w08+ppyH0oBjjA4B32hl5IOL4iHupNIMxVw9AVZ Adduz1bIWzQUizYqE8BlQRsAbgAAAowUAw4mMSn5eTm4xkoFOB90Ay++pvR1vxBSfFoc6MsCHJGh g4MsgOO+K1OLSdFGpDdxFmijkjIuNSali4gD6aJuJQVcsaMKVIXmpL2NHpgohBGGoidnFj3CsLW1 hTEeDAarq7PpsuzI5mg0WlmRbGcmgm86ndZqKccSqzAKBo9kDdqoJtPJZDqp1xfwlAdtVGg21oDA wLhtb2/TGySELlqQuHEGT94Ninnm+x89MFGJCThvsbkfl6pxcBzLVJ5uNBppTJwGbZBm4kzQRsWa OCdoo0KA43gHT/k9oxbQtrXFdYlwggy34sYZPHk3IrfLs4JdPlLWs9FzgxRXTWrfyLeonhbUxKWG q1ITp0cbK7EAmdBGREwcirKcN5qGNgCI410ECABwMiX/oagGAK21DQCYjEfjYbvTkey7SS5fmCsm 84uYOPTRRx/5/NZ4fsRUzg9BCNFMcJhyUlPz7yqrQYqr5q1bs917arXGyorNPDJONJ61KPDCNk3Z 0UYURU0AyDAnLo1ucbwrXZe6vXuT/P6fP/1bbrW87EP8dVSfQj4jU6L8ljiE0QMT+RwpLrFBigvD X716RX8ejZS7s42MD0glIardnUzbNifaAGA6nUynBpvNWaNtJwPaYKmjVN90gxz3Q+VmIRadgAuh QQodg0sFHEGbOeDOzy+jyNIGjsd4PLZs50gISFMYlwdtKAPaiJYScHEc+45M6RdneoRWIhSVqjjb H9RKmkKjm1u3btVqs7udDVE5qK00UogwGFwNLyRJtq16uZ19+wPTQ+9FroniY1W/aGM+dnlCVHJ3 lODdIOOzoiqhqFQFPRWDQhsU/PCXOjjRr43GY42JY9EGAFFUNzdx4qblrdV1kzeaoA04H1cS2mCJ HBy9O8qhG5hFbSFEXvnlttME2yYFz/L9nv48Gk00FBP/NBiMOLRRpQJuMpGgjai1uq5nnCHaqKbT CZ6m3BTFoY1oCQDHPvhLo5s+XxPmRDZruUrABW5jvQEuilYi9fnELOBUXGM+Sgk4k3NmVIDLirYI GhE0AAAnE6yImotGG1GlAcfFNKXRDdTtGPg9bKH8qfdgLRsnD4CL5jm4VMClom3+OXyUqrFsojgT F0VNC7RxvxEZ5wdtRBUFnJiuKZNuIJiaqtzDFsrTY6qF+4I3Svr++4Pn9J8qwEXRymSK6o0MlKGA szsakAAuK9dAhjYqyjifaCOqHOCkmWivaxVEsfPdg1174EoWk/uDWkJrqEKXahx8fNB5r4N+j7c2 Z/ceQjWMr1ahRtEKQlfLEqJaPUlMD8GKas0oakxtTz5tNNbr9YbRXDbyddBA2q0oiTZbG8lkQv6L mPWqBG3Dfu+rL/6TW7QRVWg5qmqQrZwZIZyqO+Ejq3Z2dkyOIyEKbWA0k4qYXnDw8cG9v7qH5htk 3Lv/pvl7J2Pdbr0oWjBQw0HGRyzi/dcwbQckjWWj2lzbkv5+Y+cGsXJPHv8DOXjerJQ2Cn+aiOY2 KZluhGvAbLC19DLpLsuBe7c3xgxtyUKA9uCNd9iQTT+LTQo4jmusjBgncG3hE2SMM+EaaNAW3wCE To6/8papIL3R/KnsU3oHUCbd2JKF/4hwKH1lK23ZODm5rAcfH2CE7/+H+xzaAICg7cEbb1swTsM1 VkrGabm28AkM4/KgjXANAJ48/ociQlG9ArxDU++U0tYqiN4kwOYrTtLKLodl45TzsnLRqEIoE+Cm 02xokADOGG1XHzLs54pGvVs2UUHdoSYmoAS6qYqVKSe1BOL6yjJZNk7Wd4U0GlUIAcCDh+/B3NRw gBOdmj4TJ2rGuOxcAwA8Hz2YaA/r0lu2ctFGFALgzE2AV7qlFiuEtvMmSvOltGycsl5ZbTSq0Zxx cx+nj+CyAQ41IPuAA1aMinKY01u2J4//IY63A+kh5d6kmUyAg22zTGR4A4d5rFRBIlN8l55rRJmu rFk0KhUGgO9ePKaAQygCNePIhDgd4wSn1lxtgRnjVFybfXWzCXPGpVq2cNAG86lwpQAua3zjY76b +US2Ck2xcSJyOPEST/GjMr+yWaJRpc47p+edk+34JuEdQkh6rjtRVKvz0+JQA1ANkBJP9XqjXpfP jMNQA4gM58mvrm42VlYxQhihiCnhRnwDarUnX/99kly22+3Qekgp96lF6qbYyNTOm7wO8SnbMq9D fYn0NbWNRjUigepPiQkCPE0JVCeZByJZE6c3a6xW6i3Vn7Z3bjz5+u9mPzNHvQQon/3WLitdYGRq nSZf+viUa5mlry+VJqjJEY1qRALVL8k/Hjz8KTlzQB2o1gBgYrwXJswDVcMFraDm2vbODUDoyVd/ d3T0mM6RChlt4DFEtSdJEd4tfzppWe2MqmWWtb5SiZV1Eo0aaPb5xM2lDTikME7q1FT5uFSoERDT XhE+2qiK7rp5msIx3RymyZfvhtdfp+Wrr0a0sgVEoya6whzWekWWceaBJzCY03Dtydd/z0GNqEJo Iyqu6+ZsCpd0c3tVlmn6myH0XzfAffarzwqIRjNphrl7bzySbsJBNZlkCFe5E0uj5MokEqhhjBGS nJda3TH0Irpufp64oVtBV2U57nbzi7RMQE/VwccHP//dz9sngVxfI8xJGZd6+vLJ4VekJ2MMUqgR Vc6ycXK7HNVJa+SlW9FPm0oDzqJxKl1fQ9FodHd7Nzya6xzc/oMP6AvGY8l0kJPDr2SXOn0lddXR RuWkA7tqjVx083BJqmtnrBtnuQHHjY1WrbKpyUGbzMPSoI0o5zV12BqWe/N620S30OMyC1LOxqnc tqjmuhobnTdM1SqL0/7TSVrZJUMbuNiG2lVr2NAt9rsRdrVuACeNs5Qz4FTTPqp1fXNK3Gp/ydBG ZHdNnbdGttm8ZY3pVOJud9g4xLFWKmTTKXXaR4lLFz2LXtnqDo8aKus1LQL0pnm30i9G4L2/iGsT eJUNZb4IYTnqa6JltWyiDK9pQQ1iFJl6DkWlCjl+KejahFxlQ4mJNo2WoL4mIr1le3u77IL4kMk1 LY71KZFp6ZaNVYDxadHtE2CVDWW3CGHpQ1R6Jy99Tan0NS3UxuroFpp/Di0b5aF9QquyofIsia8u 0FPFdZhrwBV9B8kjU28TPrIqkODFZ/sEUmVzZYpGRVWjxSUGAAADbUlEQVRxDpCJpHdy5S6utcSa ejAHvHcLKhSVqvRnu39LW5WHvKsl8VWpr6H099SSVVYjtqZ+bqKFMdPQQlGVyuoN5aI/8HvA+QZt gdfXUIb31HJU1kQ+b6KIfmWYoahUpfj50geOSzetGuWMRqVagqjN3C4sQWUNRdDmZ8i4DtWxbKx8 3uqBROthjjAUukFbpaO2rLdVpStrqHi2V4qnIWP029/+tvT71k5+ukJo6A/qBihmu3BeQVXZUNbd poqVNZTYJkVXNir9/FdrFW3fwozWwwlhiohGpQo5JBeVs9uEc33dqpQhY0/nmRahQiO10Cwbq9Lv ds/bhYcZkkvlpNssX4iqaZZCK+v1LPoi5LxpAsmy6VVi7/cTjYoK/4Z3+0QMv76GMmmWgiprub9b OHJrZEofGDVUWfGLt2hUVOAhm3OzH3h9DWXYLAVVtvLeDdxtdhy+ZePk8/Fe0uFVvMJ0NMXlMcKs r6GyNovzylbeu4EL8FfFsnHyloAjlu3BXz4oF20QpKMpNEUbYH0NZdEsziu7DHSDfPd5yAMIevlZ klliNCpVODe8n1H1cOprLut7ym1ll4Rudvd5mHM+Mqnoru/rlPhsCuGG9+n3Q6ivofLfUw4rW+EZ IZyyDi1X17JxKmhMPZBEm0rlTovx33kqMU3EVbO05zuz5zwPb3noBsY9vooDCHo5v9XLmvZhrhIn wZX1XAwccG6bhW4lkqe+SxKZEpnEpxUdQNDLbQIutESbSiVuplBW5wk2RC2oWXLWd6noBtrmWIIs m0ZO+v3Bxwff/cV3ASbaVPJ8t4eQzQgQcMGOGi9VZEokDdNC6JdFK2fkEn40KpWfeC2obEZQIaqH O8u6vsvm3UAI05bbsnGyTsBVJRqVqmg7E2A2IxAH58002NV3GdYqSBXPz8R9TbhGlfUpF/jYqLkK sjMhd6FyHZz/lsla3yX0bkQIodfHsrHK9JQLZxFCfhVhZ0JGG5Tq4Eppmaz1XU66UcYH2y8LlWEn qHQ0KlURWyoE3oVet4HjTPVdtsiUy/6Gk3z1L03dlyYaFeXqilcCbVQ++3kILWNY36UaMxXbvfSN HkuUqu4VHRs1VP7xxKCGRw3lbRQ1BLSBcX2XJzKVtvuyHv1rImndly8aFZUnWAtweNRQHkLUQNBG ZFLf/x+azoveae3jGgAAAABJRU5ErkJggg== "
+       id="image3787"
+       x="2.5000002"
+       y="400.93362" />
+  </g>
+</svg>
diff --git a/third_party/nanopb/docs/logo/logo16px.png b/third_party/nanopb/docs/logo/logo16px.png
new file mode 100644
index 0000000..8db0e2e
--- /dev/null
+++ b/third_party/nanopb/docs/logo/logo16px.png
Binary files differ
diff --git a/third_party/nanopb/docs/logo/logo48px.png b/third_party/nanopb/docs/logo/logo48px.png
new file mode 100644
index 0000000..b598c01
--- /dev/null
+++ b/third_party/nanopb/docs/logo/logo48px.png
Binary files differ
diff --git a/third_party/nanopb/docs/lsr.css b/third_party/nanopb/docs/lsr.css
new file mode 100644
index 0000000..429bce5
--- /dev/null
+++ b/third_party/nanopb/docs/lsr.css
@@ -0,0 +1,240 @@
+/*
+Author: Peter Parente
+Date: 2008/01/22
+Version: 1.0 (modified)
+Copyright: This stylesheet has been placed in the public domain - free to edit and use for all uses.
+*/
+
+body {
+  font: 100% sans-serif;
+  background: #ffffff;
+  color: black;
+  margin: 2em;
+  padding: 0em 2em;
+}
+
+p.topic-title {
+  font-weight: bold;
+}
+
+table.docinfo {
+  text-align: left;
+  margin: 2em 0em;
+}
+
+a[href] {
+  color: #436976;
+  background-color: transparent;
+}
+
+a.toc-backref {
+  text-decoration: none;
+}
+
+h1 a[href] {
+  color: #003a6b;
+  text-decoration: none;
+  background-color: transparent;
+}
+
+a.strong {
+  font-weight: bold;
+}
+
+img {
+  margin: 0;
+  border: 0;
+}
+
+p {
+  margin: 0.5em 0 1em 0;
+  line-height: 1.5em;
+}
+
+p a:visited {
+  color: purple;
+  background-color: transparent;
+}
+
+p a:active {
+  color: red;
+  background-color: transparent;
+}
+
+a:hover {
+  text-decoration: none;
+}
+
+p img {
+  border: 0;
+  margin: 0;
+}
+
+p.rubric {
+  font-weight: bold;
+  font-style: italic;
+}
+
+em {
+  font-style: normal;
+  font-family: monospace;
+  font-weight: bold;
+}
+
+pre {
+  border-left: 3px double #aaa;
+  padding: 5px 10px;
+  background-color: #f6f6f6;
+}
+
+h1.title {
+  color: #003a6b;
+  font-size: 180%;
+  margin-bottom: 0em;
+}
+
+h2.subtitle {
+  color: #003a6b;
+  border-bottom: 0px;
+}
+
+h1, h2, h3, h4, h5, h6 {
+  color: #555;
+  background-color: transparent;
+  margin: 0em;
+  padding-top: 0.5em;
+}
+
+h1 {
+  font-size: 150%;
+  margin-bottom: 0.5em;
+  border-bottom: 2px solid #aaa;
+}
+
+h2 {
+  font-size: 130%;
+  margin-bottom: 0.5em;
+  border-bottom: 1px solid #aaa;
+}
+
+h3 {
+  font-size: 120%;
+  margin-bottom: 0.5em;
+}
+
+h4 {
+  font-size: 110%;
+  font-weight: bold;
+  margin-bottom: 0.5em;
+}
+
+h5 {
+  font-size: 105%;
+  font-weight: bold;
+  margin-bottom: 0.5em;
+}
+
+h6 {
+  font-size: 100%;
+  font-weight: bold;
+  margin-bottom: 0.5em;
+}
+
+dt {
+  font-style: italic;
+}
+
+dd {
+  margin-bottom: 1.5em;
+}
+
+div.admonition, div.note, div.tip, div.caution, div.important {
+  margin: 2em 2em;
+  padding: 0em 1em;
+  border-top: 1px solid #aaa;
+  border-left: 1px solid #aaa;
+  border-bottom: 2px solid #555;
+  border-right: 2px solid #555;
+}
+
+div.important {
+  background: transparent url('../images/important.png') 10px 2px no-repeat;
+}
+
+div.caution {
+  background: transparent url('../images/caution.png') 10px 2px no-repeat;
+}
+
+div.note {
+  background: transparent url('../images/note.png') 10px 2px no-repeat;
+}
+
+div.tip {
+  background: transparent url('../images/tip.png') 10px 2px no-repeat;
+}
+
+div.admonition-example {
+  background: transparent url('../images/tip.png') 10px 2px no-repeat;
+}
+
+div.admonition-critical-example {
+  background: transparent url('../images/important.png') 10px 2px no-repeat;
+}
+
+p.admonition-title {
+  font-weight: bold;
+  border-bottom: 1px solid #aaa;
+  padding-left: 30px;
+}
+
+table.docutils {
+  text-align: left;
+  border: 1px solid gray;
+  border-collapse: collapse;
+  margin: 1.5em 0em;
+}
+
+table.docutils caption {
+  font-style: italic;
+}
+
+table.docutils td, table.docutils th {
+  padding: 0.25em 0.5em;
+}
+
+th.field-name {
+   text-align: right;
+   width: 15em;
+}
+
+table.docutils th {
+  font-family: monospace;
+  background-color: #f6f6f6;
+  vertical-align: middle;
+}
+
+table.field-list {
+  border: none;  
+}
+
+div.sidebar {
+  margin: 2em 2em 2em 0em;
+  padding: 0em 1em;
+  border-top: 1px solid #aaa;
+  border-left: 1px solid #aaa;
+  border-bottom: 2px solid #555;
+  border-right: 2px solid #555;
+}
+
+p.sidebar-title {
+  margin-bottom: 0em;
+  color: #003a6b;
+  border-bottom: 1px solid #aaa;
+  font-weight: bold;
+}
+
+p.sidebar-subtitle {
+  margin-top: 0em;
+  font-style: italic;
+  color: #003a6b;
+}
diff --git a/third_party/nanopb/docs/menu.rst b/third_party/nanopb/docs/menu.rst
new file mode 100644
index 0000000..2c110de
--- /dev/null
+++ b/third_party/nanopb/docs/menu.rst
@@ -0,0 +1,13 @@
+.. sidebar :: Documentation index
+
+    1) `Overview`_
+    2) `Concepts`_
+    3) `API reference`_
+    4) `Security model`_
+    5) `Migration from older versions`_
+    
+.. _`Overview`: index.html
+.. _`Concepts`: concepts.html
+.. _`API reference`: reference.html
+.. _`Security model`: security.html
+.. _`Migration from older versions`: migration.html
diff --git a/third_party/nanopb/docs/migration.rst b/third_party/nanopb/docs/migration.rst
new file mode 100644
index 0000000..cd5911f
--- /dev/null
+++ b/third_party/nanopb/docs/migration.rst
@@ -0,0 +1,276 @@
+=====================================
+Nanopb: Migration from older versions
+=====================================
+
+.. include :: menu.rst
+
+This document details all the breaking changes that have been made to nanopb
+since its initial release. For each change, the rationale and required
+modifications of user applications are explained. Also any error indications
+are included, in order to make it easier to find this document.
+
+.. contents ::
+
+Nanopb-0.3.5 (2016-02-13)
+=========================
+
+Add support for platforms without uint8_t
+-----------------------------------------
+**Rationale:** Some platforms cannot access 8-bit sized values directly, and
+do not define *uint8_t*. Nanopb previously didn't support these platforms.
+
+**Changes:** References to *uint8_t* were replaced with several alternatives,
+one of them being a new *pb_byte_t* typedef. This in turn uses *uint_least8_t*
+which means the smallest available type.
+
+**Required actions:** If your platform does not have a standards-compliant
+*stdint.h*, it may lack the definition for *[u]int_least8_t*. This must be
+added manually, example can be found in *extra/pb_syshdr.h*.
+
+**Error indications:** Compiler error: "unknown type name 'uint_least8_t'".
+
+Nanopb-0.3.2 (2015-01-24)
+=========================
+
+Add support for OneOfs
+----------------------
+**Rationale:** Previously nanopb did not support the *oneof* construct in
+*.proto* files. Those fields were generated as regular *optional* fields.
+
+**Changes:** OneOfs are now generated as C unions. Callback fields are not
+supported inside oneof and generator gives an error.
+
+**Required actions:** The generator option *no_unions* can be used to restore old
+behaviour and to allow callbacks to be used. To use unions, one change is
+needed: use *which_xxxx* field to detect which field is present, instead
+of *has_xxxx*. Compare the value against *MyStruct_myfield_tag*.
+
+**Error indications:** Generator error: "Callback fields inside of oneof are
+not supported". Compiler error: "Message" has no member named "has_xxxx".
+
+Nanopb-0.3.0 (2014-08-26)
+=========================
+
+Separate field iterator logic to pb_common.c
+--------------------------------------------
+**Rationale:** Originally, the field iteration logic was simple enough to be
+duplicated in *pb_decode.c* and *pb_encode.c*. New field types have made the
+logic more complex, which required the creation of a new file to contain the
+common functionality.
+
+**Changes:** There is a new file, *pb_common.c*, which must be included in
+builds.
+
+**Required actions:** Add *pb_common.c* to build rules. This file is always
+required. Either *pb_decode.c* or *pb_encode.c* can still be left out if some
+functionality is not needed.
+
+**Error indications:** Linker error: undefined reference to
+*pb_field_iter_begin*, *pb_field_iter_next* or similar.
+
+Change data type of field counts to pb_size_t
+---------------------------------------------
+**Rationale:** Often nanopb is used with small arrays, such as 255 items or
+less. Using a full *size_t* field to store the array count wastes memory if
+there are many arrays. There already exists parameters *PB_FIELD_16BIT* and
+*PB_FIELD_32BIT* which tell nanopb what is the maximum size of arrays in use.
+
+**Changes:** Generator will now use *pb_size_t* for the array *_count* fields.
+The size of the type will be controlled by the *PB_FIELD_16BIT* and
+*PB_FIELD_32BIT* compilation time options.
+
+**Required actions:** Regenerate all *.pb.h* files. In some cases casts to the
+*pb_size_t* type may need to be added in the user code when accessing the
+*_count* fields.
+
+**Error indications:** Incorrect data at runtime, crashes. But note that other
+changes in the same version already require regenerating the files and have
+better indications of errors, so this is only an issue for development
+versions.
+
+Renamed some macros and identifiers
+-----------------------------------
+**Rationale:** Some names in nanopb core were badly chosen and conflicted with
+ISO C99 reserved names or lacked a prefix. While they haven't caused trouble
+so far, it is reasonable to switch to non-conflicting names as these are rarely
+used from user code.
+
+**Changes:** The following identifier names have changed:
+
+  * Macros:
+  
+    * STATIC_ASSERT(x) -> PB_STATIC_ASSERT(x)
+    * UNUSED(x) -> PB_UNUSED(x)
+  
+  * Include guards:
+  
+    * _PB_filename_ -> PB_filename_INCLUDED
+  
+  * Structure forward declaration tags:
+  
+    * _pb_field_t -> pb_field_s
+    * _pb_bytes_array_t -> pb_bytes_array_s
+    * _pb_callback_t -> pb_callback_s
+    * _pb_extension_type_t -> pb_extension_type_s
+    * _pb_extension_t -> pb_extension_s
+    * _pb_istream_t -> pb_istream_s
+    * _pb_ostream_t -> pb_ostream_s
+
+**Required actions:** Regenerate all *.pb.c* files. If you use any of the above
+identifiers in your application code, perform search-replace to the new name.
+
+**Error indications:** Compiler errors on lines with the macro/type names.
+
+Nanopb-0.2.9 (2014-08-09)
+=========================
+
+Change semantics of generator -e option
+---------------------------------------
+**Rationale:** Some compilers do not accept filenames with two dots (like
+in default extension .pb.c). The *-e* option to the generator allowed changing
+the extension, but not skipping the extra dot.
+
+**Changes:** The *-e* option in generator will no longer add the prepending
+dot. The default value has been adjusted accordingly to *.pb.c* to keep the
+default behaviour the same as before.
+
+**Required actions:** Only if using the generator -e option. Add dot before
+the parameter value on the command line.
+
+**Error indications:** File not found when trying to compile generated files.
+
+Nanopb-0.2.7 (2014-04-07)
+=========================
+
+Changed pointer-type bytes field datatype
+-----------------------------------------
+**Rationale:** In the initial pointer encoding support since nanopb-0.2.5,
+the bytes type used a separate *pb_bytes_ptr_t* type to represent *bytes*
+fields. This made it easy to encode data from a separate, user-allocated
+buffer. However, it made the internal logic more complex and was inconsistent
+with the other types.
+
+**Changes:** Dynamically allocated bytes fields now have the *pb_bytes_array_t*
+type, just like statically allocated ones.
+
+**Required actions:** Only if using pointer-type fields with the bytes datatype.
+Change any access to *msg->field.size* to *msg->field->size*. Change any
+allocation to reserve space of amount *PB_BYTES_ARRAY_T_ALLOCSIZE(n)*. If the
+data pointer was begin assigned from external source, implement the field using
+a callback function instead.
+
+**Error indications:** Compiler error: unknown type name *pb_bytes_ptr_t*.
+
+Nanopb-0.2.4 (2013-11-07)
+=========================
+
+Remove the NANOPB_INTERNALS compilation option
+----------------------------------------------
+**Rationale:** Having the option in the headers required the functions to
+be non-static, even if the option is not used. This caused errors on some
+static analysis tools.
+
+**Changes:** The *#ifdef* and associated functions were removed from the
+header.
+
+**Required actions:** Only if the *NANOPB_INTERNALS* option was previously
+used. Actions are as listed under nanopb-0.1.3 and nanopb-0.1.6.
+
+**Error indications:** Compiler warning: implicit declaration of function
+*pb_dec_string*, *pb_enc_string*, or similar.
+
+Nanopb-0.2.1 (2013-04-14)
+=========================
+
+Callback function signature
+---------------------------
+**Rationale:** Previously the auxilary data to field callbacks was passed
+as *void\**. This allowed passing of any data, but made it unnecessarily
+complex to return a pointer from callback.
+
+**Changes:** The callback function parameter was changed to *void\*\**.
+
+**Required actions:** You can continue using the old callback style by
+defining *PB_OLD_CALLBACK_STYLE*. Recommended action is to:
+
+  * Change the callback signatures to contain *void\*\** for decoders and
+    *void \* const \** for encoders.
+  * Change the callback function body to use *\*arg* instead of *arg*.
+
+**Error indications:** Compiler warning: assignment from incompatible
+pointer type, when initializing *funcs.encode* or *funcs.decode*.
+
+Nanopb-0.2.0 (2013-03-02)
+=========================
+
+Reformatted generated .pb.c file using macros
+---------------------------------------------
+**Rationale:** Previously the generator made a list of C *pb_field_t*
+initializers in the .pb.c file. This led to a need to regenerate all .pb.c
+files after even small changes to the *pb_field_t* definition.
+
+**Changes:** Macros were added to pb.h which allow for cleaner definition
+of the .pb.c contents. By changing the macro definitions, changes to the
+field structure are possible without breaking compatibility with old .pb.c
+files.
+
+**Required actions:** Regenerate all .pb.c files from the .proto sources.
+
+**Error indications:** Compiler warning: implicit declaration of function
+*pb_delta_end*.
+
+Changed pb_type_t definitions
+-----------------------------
+**Rationale:** The *pb_type_t* was previously an enumeration type. This
+caused warnings on some compilers when using bitwise operations to set flags
+inside the values.
+
+**Changes:** The *pb_type_t* was changed to *typedef uint8_t*. The values
+were changed to *#define*. Some value names were changed for consistency.
+
+**Required actions:** Only if you directly access the `pb_field_t` contents
+in your own code, something which is not usually done. Needed changes:
+
+  * Change *PB_HTYPE_ARRAY* to *PB_HTYPE_REPEATED*.
+  * Change *PB_HTYPE_CALLBACK* to *PB_ATYPE()* and *PB_ATYPE_CALLBACK*.
+
+**Error indications:** Compiler error: *PB_HTYPE_ARRAY* or *PB_HTYPE_CALLBACK*
+undeclared.
+
+Nanopb-0.1.6 (2012-09-02)
+=========================
+
+Refactored field decoder interface
+----------------------------------
+**Rationale:** Similarly to field encoders in nanopb-0.1.3.
+
+**Changes:** New functions with names *pb_decode_\** were added.
+
+**Required actions:** By defining NANOPB_INTERNALS, you can still keep using
+the old functions. Recommended action is to replace any calls with the newer
+*pb_decode_\** equivalents.
+
+**Error indications:** Compiler warning: implicit declaration of function
+*pb_dec_string*, *pb_dec_varint*, *pb_dec_submessage* or similar.
+
+Nanopb-0.1.3 (2012-06-12)
+=========================
+
+Refactored field encoder interface
+----------------------------------
+**Rationale:** The old *pb_enc_\** functions were designed mostly for the
+internal use by the core. Because they are internally accessed through
+function pointers, their signatures had to be common. This led to a confusing
+interface for external users.
+
+**Changes:** New functions with names *pb_encode_\** were added. These have
+easier to use interfaces. The old functions are now only thin wrappers for
+the new interface.
+
+**Required actions:** By defining NANOPB_INTERNALS, you can still keep using
+the old functions. Recommended action is to replace any calls with the newer
+*pb_encode_\** equivalents.
+
+**Error indications:** Compiler warning: implicit declaration of function
+*pb_enc_string*, *pb_enc_varint, *pb_enc_submessage* or similar.
+
diff --git a/third_party/nanopb/docs/reference.rst b/third_party/nanopb/docs/reference.rst
new file mode 100644
index 0000000..ef3867a
--- /dev/null
+++ b/third_party/nanopb/docs/reference.rst
@@ -0,0 +1,770 @@
+=====================
+Nanopb: API reference
+=====================
+
+.. include :: menu.rst
+
+.. contents ::
+
+
+
+
+Compilation options
+===================
+The following options can be specified in one of two ways:
+
+1. Using the -D switch on the C compiler command line.
+2. By #defining them at the top of pb.h.
+
+You must have the same settings for the nanopb library and all code that
+includes pb.h.
+
+============================  ================================================
+PB_NO_PACKED_STRUCTS           Disable packed structs. Increases RAM usage but
+                               is necessary on some platforms that do not
+                               support unaligned memory access.
+PB_ENABLE_MALLOC               Set this to enable dynamic allocation support
+                               in the decoder.
+PB_MAX_REQUIRED_FIELDS         Maximum number of required fields to check for
+                               presence. Default value is 64. Increases stack
+                               usage 1 byte per every 8 fields. Compiler
+                               warning will tell if you need this.
+PB_FIELD_16BIT                 Add support for tag numbers > 255 and fields
+                               larger than 255 bytes or 255 array entries.
+                               Increases code size 3 bytes per each field.
+                               Compiler error will tell if you need this.
+PB_FIELD_32BIT                 Add support for tag numbers > 65535 and fields
+                               larger than 65535 bytes or 65535 array entries.
+                               Increases code size 9 bytes per each field.
+                               Compiler error will tell if you need this.
+PB_NO_ERRMSG                   Disables the support for error messages; only
+                               error information is the true/false return
+                               value. Decreases the code size by a few hundred
+                               bytes.
+PB_BUFFER_ONLY                 Disables the support for custom streams. Only
+                               supports encoding and decoding with memory
+                               buffers. Speeds up execution and decreases code
+                               size slightly.
+PB_OLD_CALLBACK_STYLE          Use the old function signature (void\* instead
+                               of void\*\*) for callback fields. This was the
+                               default until nanopb-0.2.1.
+PB_SYSTEM_HEADER               Replace the standard header files with a single
+                               header file. It should define all the required
+                               functions and typedefs listed on the
+                               `overview page`_. Value must include quotes,
+                               for example *#define PB_SYSTEM_HEADER "foo.h"*.
+============================  ================================================
+
+The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow
+raising some datatype limits to suit larger messages. Their need is recognized
+automatically by C-preprocessor #if-directives in the generated .pb.h files.
+The default setting is to use the smallest datatypes (least resources used).
+
+.. _`overview page`: index.html#compiler-requirements
+
+
+Proto file options
+==================
+The generator behaviour can be adjusted using these options, defined in the
+'nanopb.proto' file in the generator folder:
+
+============================  ================================================
+max_size                       Allocated size for *bytes* and *string* fields.
+max_count                      Allocated number of entries in arrays
+                               (*repeated* fields).
+int_size                       Override the integer type of a field.
+                               (To use e.g. uint8_t to save RAM.)
+type                           Type of the generated field. Default value
+                               is *FT_DEFAULT*, which selects automatically.
+                               You can use *FT_CALLBACK*, *FT_POINTER*,
+                               *FT_STATIC*, *FT_IGNORE*, or *FT_INLINE* to
+                               force a callback field, a dynamically
+                               allocated field, a static field, to
+                               completely ignore the field or to
+                               generate an inline bytes field.
+long_names                     Prefix the enum name to the enum value in
+                               definitions, i.e. *EnumName_EnumValue*. Enabled
+                               by default.
+packed_struct                  Make the generated structures packed.
+                               NOTE: This cannot be used on CPUs that break
+                               on unaligned accesses to variables.
+skip_message                   Skip the whole message from generation.
+no_unions                      Generate 'oneof' fields as optional fields
+                               instead of C unions.
+msgid                          Specifies a unique id for this message type.
+                               Can be used by user code as an identifier.
+anonymous_oneof                Generate 'oneof' fields as anonymous unions.
+============================  ================================================
+
+These options can be defined for the .proto files before they are converted
+using the nanopb-generatory.py. There are three ways to define the options:
+
+1. Using a separate .options file.
+   This is the preferred way as of nanopb-0.2.1, because it has the best
+   compatibility with other protobuf libraries.
+2. Defining the options on the command line of nanopb_generator.py.
+   This only makes sense for settings that apply to a whole file.
+3. Defining the options in the .proto file using the nanopb extensions.
+   This is the way used in nanopb-0.1, and will remain supported in the
+   future. It however sometimes causes trouble when using the .proto file
+   with other protobuf libraries.
+
+The effect of the options is the same no matter how they are given. The most
+common purpose is to define maximum size for string fields in order to
+statically allocate them.
+
+Defining the options in a .options file
+---------------------------------------
+The preferred way to define options is to have a separate file
+'myproto.options' in the same directory as the 'myproto.proto'. ::
+
+    # myproto.proto
+    message MyMessage {
+        required string name = 1;
+        repeated int32 ids = 4;
+    }
+
+::
+
+    # myproto.options
+    MyMessage.name         max_size:40
+    MyMessage.ids          max_count:5
+
+The generator will automatically search for this file and read the
+options from it. The file format is as follows:
+
+* Lines starting with '#' or '//' are regarded as comments.
+* Blank lines are ignored.
+* All other lines should start with a field name pattern, followed by one or
+  more options. For example: *"MyMessage.myfield max_size:5 max_count:10"*.
+* The field name pattern is matched against a string of form *'Message.field'*.
+  For nested messages, the string is *'Message.SubMessage.field'*.
+* The field name pattern may use the notation recognized by Python fnmatch():
+
+  - *\** matches any part of string, like 'Message.\*' for all fields
+  - *\?* matches any single character
+  - *[seq]* matches any of characters 's', 'e' and 'q'
+  - *[!seq]* matches any other character
+
+* The options are written as *'option_name:option_value'* and several options
+  can be defined on same line, separated by whitespace.
+* Options defined later in the file override the ones specified earlier, so
+  it makes sense to define wildcard options first in the file and more specific
+  ones later.
+  
+If preferred, the name of the options file can be set using the command line
+switch *-f* to nanopb_generator.py.
+
+Defining the options on command line
+------------------------------------
+The nanopb_generator.py has a simple command line option *-s OPTION:VALUE*.
+The setting applies to the whole file that is being processed.
+
+Defining the options in the .proto file
+---------------------------------------
+The .proto file format allows defining custom options for the fields.
+The nanopb library comes with *nanopb.proto* which does exactly that, allowing
+you do define the options directly in the .proto file::
+
+    import "nanopb.proto";
+    
+    message MyMessage {
+        required string name = 1 [(nanopb).max_size = 40];
+        repeated int32 ids = 4   [(nanopb).max_count = 5];
+    }
+
+A small complication is that you have to set the include path of protoc so that
+nanopb.proto can be found. This file, in turn, requires the file
+*google/protobuf/descriptor.proto*. This is usually installed under
+*/usr/include*. Therefore, to compile a .proto file which uses options, use a
+protoc command similar to::
+
+    protoc -I/usr/include -Inanopb/generator -I. -omessage.pb message.proto
+
+The options can be defined in file, message and field scopes::
+
+    option (nanopb_fileopt).max_size = 20; // File scope
+    message Message
+    {
+        option (nanopb_msgopt).max_size = 30; // Message scope
+        required string fieldsize = 1 [(nanopb).max_size = 40]; // Field scope
+    }
+
+
+
+
+
+
+
+
+
+pb.h
+====
+
+pb_byte_t
+---------
+Type used for storing byte-sized data, such as raw binary input and bytes-type fields. ::
+
+    typedef uint_least8_t pb_byte_t;
+
+For most platforms this is equivalent to `uint8_t`. Some platforms however do not support
+8-bit variables, and on those platforms 16 or 32 bits need to be used for each byte.
+
+pb_type_t
+---------
+Type used to store the type of each field, to control the encoder/decoder behaviour. ::
+
+    typedef uint_least8_t pb_type_t;
+
+The low-order nibble of the enumeration values defines the function that can be used for encoding and decoding the field data:
+
+=========================== ===== ================================================
+LTYPE identifier            Value Storage format
+=========================== ===== ================================================
+PB_LTYPE_VARINT             0x00  Integer.
+PB_LTYPE_UVARINT            0x01  Unsigned integer.
+PB_LTYPE_SVARINT            0x02  Integer, zigzag encoded.
+PB_LTYPE_FIXED32            0x03  32-bit integer or floating point.
+PB_LTYPE_FIXED64            0x04  64-bit integer or floating point.
+PB_LTYPE_BYTES              0x05  Structure with *size_t* field and byte array.
+PB_LTYPE_STRING             0x06  Null-terminated string.
+PB_LTYPE_SUBMESSAGE         0x07  Submessage structure.
+PB_LTYPE_EXTENSION          0x08  Point to *pb_extension_t*.
+PB_LTYPE_FIXED_LENGTH_BYTES 0x09  Inline *pb_byte_t* array of fixed size.
+=========================== ===== ================================================
+
+The bits 4-5 define whether the field is required, optional or repeated:
+
+==================== ===== ================================================
+HTYPE identifier     Value Field handling
+==================== ===== ================================================
+PB_HTYPE_REQUIRED    0x00  Verify that field exists in decoded message.
+PB_HTYPE_OPTIONAL    0x10  Use separate *has_<field>* boolean to specify
+                           whether the field is present.
+                           (Unless it is a callback)
+PB_HTYPE_REPEATED    0x20  A repeated field with preallocated array.
+                           Separate *<field>_count* for number of items.
+                           (Unless it is a callback)
+==================== ===== ================================================
+
+The bits 6-7 define the how the storage for the field is allocated:
+
+==================== ===== ================================================
+ATYPE identifier     Value Allocation method
+==================== ===== ================================================
+PB_ATYPE_STATIC      0x00  Statically allocated storage in the structure.
+PB_ATYPE_CALLBACK    0x40  A field with dynamic storage size. Struct field
+                           actually contains a pointer to a callback
+                           function.
+==================== ===== ================================================
+
+
+pb_field_t
+----------
+Describes a single structure field with memory position in relation to others. The descriptions are usually autogenerated. ::
+
+    typedef struct pb_field_s pb_field_t;
+    struct pb_field_s {
+        pb_size_t tag;
+        pb_type_t type;
+        pb_size_t data_offset;
+        pb_ssize_t size_offset;
+        pb_size_t data_size;
+        pb_size_t array_size;
+        const void *ptr;
+    } pb_packed;
+
+:tag:           Tag number of the field or 0 to terminate a list of fields.
+:type:          LTYPE, HTYPE and ATYPE of the field.
+:data_offset:   Offset of field data, relative to the end of the previous field.
+:size_offset:   Offset of *bool* flag for optional fields or *size_t* count for arrays, relative to field data.
+:data_size:     Size of a single data entry, in bytes. For PB_LTYPE_BYTES, the size of the byte array inside the containing structure. For PB_HTYPE_CALLBACK, size of the C data type if known.
+:array_size:    Maximum number of entries in an array, if it is an array type.
+:ptr:           Pointer to default value for optional fields, or to submessage description for PB_LTYPE_SUBMESSAGE.
+
+The *uint8_t* datatypes limit the maximum size of a single item to 255 bytes and arrays to 255 items. Compiler will give error if the values are too large. The types can be changed to larger ones by defining *PB_FIELD_16BIT*.
+
+pb_bytes_array_t
+----------------
+An byte array with a field for storing the length::
+
+    typedef struct {
+        pb_size_t size;
+        pb_byte_t bytes[1];
+    } pb_bytes_array_t;
+
+In an actual array, the length of *bytes* may be different.
+
+pb_callback_t
+-------------
+Part of a message structure, for fields with type PB_HTYPE_CALLBACK::
+
+    typedef struct _pb_callback_t pb_callback_t;
+    struct _pb_callback_t {
+        union {
+            bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
+            bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
+        } funcs;
+        
+        void *arg;
+    };
+
+A pointer to the *arg* is passed to the callback when calling. It can be used to store any information that the callback might need.
+
+Previously the function received just the value of *arg* instead of a pointer to it. This old behaviour can be enabled by defining *PB_OLD_CALLBACK_STYLE*.
+
+When calling `pb_encode`_, *funcs.encode* is used, and similarly when calling `pb_decode`_, *funcs.decode* is used. The function pointers are stored in the same memory location but are of incompatible types. You can set the function pointer to NULL to skip the field.
+
+pb_wire_type_t
+--------------
+Protocol Buffers wire types. These are used with `pb_encode_tag`_. ::
+
+    typedef enum {
+        PB_WT_VARINT = 0,
+        PB_WT_64BIT  = 1,
+        PB_WT_STRING = 2,
+        PB_WT_32BIT  = 5
+    } pb_wire_type_t;
+
+pb_extension_type_t
+-------------------
+Defines the handler functions and auxiliary data for a field that extends
+another message. Usually autogenerated by *nanopb_generator.py*::
+
+    typedef struct {
+        bool (*decode)(pb_istream_t *stream, pb_extension_t *extension,
+                   uint32_t tag, pb_wire_type_t wire_type);
+        bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension);
+        const void *arg;
+    } pb_extension_type_t;
+
+In the normal case, the function pointers are *NULL* and the decoder and
+encoder use their internal implementations. The internal implementations
+assume that *arg* points to a *pb_field_t* that describes the field in question.
+
+To implement custom processing of unknown fields, you can provide pointers
+to your own functions. Their functionality is mostly the same as for normal
+callback fields, except that they get called for any unknown field when decoding.
+
+pb_extension_t
+--------------
+Ties together the extension field type and the storage for the field value::
+
+    typedef struct {
+        const pb_extension_type_t *type;
+        void *dest;
+        pb_extension_t *next;
+        bool found;
+    } pb_extension_t;
+
+:type:      Pointer to the structure that defines the callback functions.
+:dest:      Pointer to the variable that stores the field value
+            (as used by the default extension callback functions.)
+:next:      Pointer to the next extension handler, or *NULL*.
+:found:     Decoder sets this to true if the extension was found.
+
+PB_GET_ERROR
+------------
+Get the current error message from a stream, or a placeholder string if
+there is no error message::
+
+    #define PB_GET_ERROR(stream) (string expression)
+
+This should be used for printing errors, for example::
+
+    if (!pb_decode(...))
+    {
+        printf("Decode failed: %s\n", PB_GET_ERROR(stream));
+    }
+
+The macro only returns pointers to constant strings (in code memory),
+so that there is no need to release the returned pointer.
+
+PB_RETURN_ERROR
+---------------
+Set the error message and return false::
+
+    #define PB_RETURN_ERROR(stream,msg) (sets error and returns false)
+
+This should be used to handle error conditions inside nanopb functions
+and user callback functions::
+
+    if (error_condition)
+    {
+        PB_RETURN_ERROR(stream, "something went wrong");
+    }
+
+The *msg* parameter must be a constant string.
+
+
+
+pb_encode.h
+===========
+
+pb_ostream_from_buffer
+----------------------
+Constructs an output stream for writing into a memory buffer. This is just a helper function, it doesn't do anything you couldn't do yourself in a callback function. It uses an internal callback that stores the pointer in stream *state* field. ::
+
+    pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize);
+
+:buf:           Memory buffer to write into.
+:bufsize:       Maximum number of bytes to write.
+:returns:       An output stream.
+
+After writing, you can check *stream.bytes_written* to find out how much valid data there is in the buffer.
+
+pb_write
+--------
+Writes data to an output stream. Always use this function, instead of trying to call stream callback manually. ::
+
+    bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
+
+:stream:        Output stream to write to.
+:buf:           Pointer to buffer with the data to be written.
+:count:         Number of bytes to write.
+:returns:       True on success, false if maximum length is exceeded or an IO error happens.
+
+If an error happens, *bytes_written* is not incremented. Depending on the callback used, calling pb_write again after it has failed once may be dangerous. Nanopb itself never does this, instead it returns the error to user application. The builtin pb_ostream_from_buffer is safe to call again after failed write.
+
+pb_encode
+---------
+Encodes the contents of a structure as a protocol buffers message and writes it to output stream. ::
+
+    bool pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
+
+:stream:        Output stream to write to.
+:fields:        A field description array, usually autogenerated.
+:src_struct:    Pointer to the data that will be serialized.
+:returns:       True on success, false on IO error, on detectable errors in field description, or if a field encoder returns false.
+
+Normally pb_encode simply walks through the fields description array and serializes each field in turn. However, submessages must be serialized twice: first to calculate their size and then to actually write them to output. This causes some constraints for callback fields, which must return the same data on every call.
+
+pb_encode_delimited
+-------------------
+Calculates the length of the message, encodes it as varint and then encodes the message. ::
+
+    bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
+
+(parameters are the same as for `pb_encode`_.)
+
+A common way to indicate the message length in Protocol Buffers is to prefix it with a varint.
+This function does this, and it is compatible with *parseDelimitedFrom* in Google's protobuf library.
+
+.. sidebar:: Encoding fields manually
+
+    The functions with names *pb_encode_\** are used when dealing with callback fields. The typical reason for using callbacks is to have an array of unlimited size. In that case, `pb_encode`_ will call your callback function, which in turn will call *pb_encode_\** functions repeatedly to write out values.
+
+    The tag of a field must be encoded separately with `pb_encode_tag_for_field`_. After that, you can call exactly one of the content-writing functions to encode the payload of the field. For repeated fields, you can repeat this process multiple times.
+
+    Writing packed arrays is a little bit more involved: you need to use `pb_encode_tag` and specify `PB_WT_STRING` as the wire type. Then you need to know exactly how much data you are going to write, and use `pb_encode_varint`_ to write out the number of bytes before writing the actual data. Substreams can be used to determine the number of bytes beforehand; see `pb_encode_submessage`_ source code for an example.
+
+pb_get_encoded_size
+-------------------
+Calculates the length of the encoded message. ::
+
+    bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct);
+
+:size:          Calculated size of the encoded message.
+:fields:        A field description array, usually autogenerated.
+:src_struct:    Pointer to the data that will be serialized.
+:returns:       True on success, false on detectable errors in field description or if a field encoder returns false.
+
+pb_encode_tag
+-------------
+Starts a field in the Protocol Buffers binary format: encodes the field number and the wire type of the data. ::
+
+    bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number);
+
+:stream:        Output stream to write to. 1-5 bytes will be written.
+:wiretype:      PB_WT_VARINT, PB_WT_64BIT, PB_WT_STRING or PB_WT_32BIT
+:field_number:  Identifier for the field, defined in the .proto file. You can get it from field->tag.
+:returns:       True on success, false on IO error.
+
+pb_encode_tag_for_field
+-----------------------
+Same as `pb_encode_tag`_, except takes the parameters from a *pb_field_t* structure. ::
+
+    bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field);
+
+:stream:        Output stream to write to. 1-5 bytes will be written.
+:field:         Field description structure. Usually autogenerated.
+:returns:       True on success, false on IO error or unknown field type.
+
+This function only considers the LTYPE of the field. You can use it from your field callbacks, because the source generator writes correct LTYPE also for callback type fields.
+
+Wire type mapping is as follows:
+
+============================================= ============
+LTYPEs                                        Wire type
+============================================= ============
+VARINT, UVARINT, SVARINT                      PB_WT_VARINT
+FIXED64                                       PB_WT_64BIT
+STRING, BYTES, SUBMESSAGE, FIXED_LENGTH_BYTES PB_WT_STRING
+FIXED32                                       PB_WT_32BIT
+============================================= ============
+
+pb_encode_varint
+----------------
+Encodes a signed or unsigned integer in the varint_ format. Works for fields of type `bool`, `enum`, `int32`, `int64`, `uint32` and `uint64`::
+
+    bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
+
+:stream:        Output stream to write to. 1-10 bytes will be written.
+:value:         Value to encode. Just cast e.g. int32_t directly to uint64_t.
+:returns:       True on success, false on IO error.
+
+.. _varint: http://code.google.com/apis/protocolbuffers/docs/encoding.html#varints
+
+pb_encode_svarint
+-----------------
+Encodes a signed integer in the 'zig-zagged' format. Works for fields of type `sint32` and `sint64`::
+
+    bool pb_encode_svarint(pb_ostream_t *stream, int64_t value);
+
+(parameters are the same as for `pb_encode_varint`_
+
+pb_encode_string
+----------------
+Writes the length of a string as varint and then contents of the string. Works for fields of type `bytes` and `string`::
+
+    bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size);
+
+:stream:        Output stream to write to.
+:buffer:        Pointer to string data.
+:size:          Number of bytes in the string. Pass `strlen(s)` for strings.
+:returns:       True on success, false on IO error.
+
+pb_encode_fixed32
+-----------------
+Writes 4 bytes to stream and swaps bytes on big-endian architectures. Works for fields of type `fixed32`, `sfixed32` and `float`::
+
+    bool pb_encode_fixed32(pb_ostream_t *stream, const void *value);
+
+:stream:    Output stream to write to.
+:value:     Pointer to a 4-bytes large C variable, for example `uint32_t foo;`.
+:returns:   True on success, false on IO error.
+
+pb_encode_fixed64
+-----------------
+Writes 8 bytes to stream and swaps bytes on big-endian architecture. Works for fields of type `fixed64`, `sfixed64` and `double`::
+
+    bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
+
+:stream:    Output stream to write to.
+:value:     Pointer to a 8-bytes large C variable, for example `uint64_t foo;`.
+:returns:   True on success, false on IO error.
+
+pb_encode_submessage
+--------------------
+Encodes a submessage field, including the size header for it. Works for fields of any message type::
+
+    bool pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
+
+:stream:        Output stream to write to.
+:fields:        Pointer to the autogenerated field description array for the submessage type, e.g. `MyMessage_fields`.
+:src:           Pointer to the structure where submessage data is.
+:returns:       True on success, false on IO errors, pb_encode errors or if submessage size changes between calls.
+
+In Protocol Buffers format, the submessage size must be written before the submessage contents. Therefore, this function has to encode the submessage twice in order to know the size beforehand.
+
+If the submessage contains callback fields, the callback function might misbehave and write out a different amount of data on the second call. This situation is recognized and *false* is returned, but garbage will be written to the output before the problem is detected.
+
+
+
+
+
+
+
+
+
+
+
+
+pb_decode.h
+===========
+
+pb_istream_from_buffer
+----------------------
+Helper function for creating an input stream that reads data from a memory buffer. ::
+
+    pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize);
+
+:buf:           Pointer to byte array to read from.
+:bufsize:       Size of the byte array.
+:returns:       An input stream ready to use.
+
+pb_read
+-------
+Read data from input stream. Always use this function, don't try to call the stream callback directly. ::
+
+    bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);
+
+:stream:        Input stream to read from.
+:buf:           Buffer to store the data to, or NULL to just read data without storing it anywhere.
+:count:         Number of bytes to read.
+:returns:       True on success, false if *stream->bytes_left* is less than *count* or if an IO error occurs.
+
+End of file is signalled by *stream->bytes_left* being zero after pb_read returns false.
+
+pb_decode
+---------
+Read and decode all fields of a structure. Reads until EOF on input stream. ::
+
+    bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
+
+:stream:        Input stream to read from.
+:fields:        A field description array. Usually autogenerated.
+:dest_struct:   Pointer to structure where data will be stored.
+:returns:       True on success, false on IO error, on detectable errors in field description, if a field encoder returns false or if a required field is missing.
+
+In Protocol Buffers binary format, EOF is only allowed between fields. If it happens anywhere else, pb_decode will return *false*. If pb_decode returns false, you cannot trust any of the data in the structure.
+
+In addition to EOF, the pb_decode implementation supports terminating a message with a 0 byte. This is compatible with the official Protocol Buffers because 0 is never a valid field tag.
+
+For optional fields, this function applies the default value and sets *has_<field>* to false if the field is not present.
+
+If *PB_ENABLE_MALLOC* is defined, this function may allocate storage for any pointer type fields.
+In this case, you have to call `pb_release`_ to release the memory after you are done with the message.
+On error return `pb_decode` will release the memory itself.
+
+pb_decode_noinit
+----------------
+Same as `pb_decode`_, except does not apply the default values to fields. ::
+
+    bool pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
+
+(parameters are the same as for `pb_decode`_.)
+
+The destination structure should be filled with zeros before calling this function. Doing a *memset* manually can be slightly faster than using `pb_decode`_ if you don't need any default values.
+
+In addition to decoding a single message, this function can be used to merge two messages, so that
+values from previous message will remain if the new message does not contain a field.
+
+This function *will not* release the message even on error return. If you use *PB_ENABLE_MALLOC*,
+you will need to call `pb_release`_ yourself.
+
+pb_decode_delimited
+-------------------
+Same as `pb_decode`_, except that it first reads a varint with the length of the message. ::
+
+    bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
+
+(parameters are the same as for `pb_decode`_.)
+
+A common method to indicate message size in Protocol Buffers is to prefix it with a varint.
+This function is compatible with *writeDelimitedTo* in the Google's Protocol Buffers library.
+
+pb_release
+----------
+Releases any dynamically allocated fields::
+
+    void pb_release(const pb_field_t fields[], void *dest_struct);
+
+:fields:        A field description array. Usually autogenerated.
+:dest_struct:   Pointer to structure where data is stored. If NULL, function does nothing.
+
+This function is only available if *PB_ENABLE_MALLOC* is defined. It will release any
+pointer type fields in the structure and set the pointers to NULL.
+
+pb_decode_tag
+-------------
+Decode the tag that comes before field in the protobuf encoding::
+
+    bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof);
+
+:stream:        Input stream to read from.
+:wire_type:     Pointer to variable where to store the wire type of the field.
+:tag:           Pointer to variable where to store the tag of the field.
+:eof:           Pointer to variable where to store end-of-file status.
+:returns:       True on success, false on error or EOF.
+
+When the message (stream) ends, this function will return false and set *eof* to true. On other
+errors, *eof* will be set to false.
+
+pb_skip_field
+-------------
+Remove the data for a field from the stream, without actually decoding it::
+
+    bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
+
+:stream:        Input stream to read from.
+:wire_type:     Type of field to skip.
+:returns:       True on success, false on IO error.
+
+.. sidebar:: Decoding fields manually
+    
+    The functions with names beginning with *pb_decode_* are used when dealing with callback fields. The typical reason for using callbacks is to have an array of unlimited size. In that case, `pb_decode`_ will call your callback function repeatedly, which can then store the values into e.g. filesystem in the order received in.
+
+    For decoding numeric (including enumerated and boolean) values, use `pb_decode_varint`_, `pb_decode_svarint`_, `pb_decode_fixed32`_ and `pb_decode_fixed64`_. They take a pointer to a 32- or 64-bit C variable, which you may then cast to smaller datatype for storage.
+
+    For decoding strings and bytes fields, the length has already been decoded. You can therefore check the total length in *stream->bytes_left* and read the data using `pb_read`_.
+
+    Finally, for decoding submessages in a callback, simply use `pb_decode`_ and pass it the *SubMessage_fields* descriptor array.
+
+pb_decode_varint
+----------------
+Read and decode a varint_ encoded integer. ::
+
+    bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
+
+:stream:        Input stream to read from. 1-10 bytes will be read.
+:dest:          Storage for the decoded integer. Value is undefined on error.
+:returns:       True on success, false if value exceeds uint64_t range or an IO error happens.
+
+pb_decode_svarint
+-----------------
+Similar to `pb_decode_varint`_, except that it performs zigzag-decoding on the value. This corresponds to the Protocol Buffers *sint32* and *sint64* datatypes. ::
+
+    bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
+
+(parameters are the same as `pb_decode_varint`_)
+
+pb_decode_fixed32
+-----------------
+Decode a *fixed32*, *sfixed32* or *float* value. ::
+
+    bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
+
+:stream:        Input stream to read from. 4 bytes will be read.
+:dest:          Pointer to destination *int32_t*, *uint32_t* or *float*.
+:returns:       True on success, false on IO errors.
+
+This function reads 4 bytes from the input stream.
+On big endian architectures, it then reverses the order of the bytes.
+Finally, it writes the bytes to *dest*.
+
+pb_decode_fixed64
+-----------------
+Decode a *fixed64*, *sfixed64* or *double* value. ::
+
+    bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
+
+:stream:        Input stream to read from. 8 bytes will be read.
+:dest:          Pointer to destination *int64_t*, *uint64_t* or *double*.
+:returns:       True on success, false on IO errors.
+
+Same as `pb_decode_fixed32`_, except this reads 8 bytes.
+
+pb_make_string_substream
+------------------------
+Decode the length for a field with wire type *PB_WT_STRING* and create a substream for reading the data. ::
+
+    bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
+
+:stream:        Original input stream to read the length and data from.
+:substream:     New substream that has limited length. Filled in by the function.
+:returns:       True on success, false if reading the length fails.
+
+This function uses `pb_decode_varint`_ to read an integer from the stream. This is interpreted as a number of bytes, and the substream is set up so that its `bytes_left` is initially the same as the length, and its callback function and state the same as the parent stream.
+
+pb_close_string_substream
+-------------------------
+Close the substream created with `pb_make_string_substream`_. ::
+
+    void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
+
+:stream:        Original input stream to read the length and data from.
+:substream:     Substream to close
+
+This function copies back the state from the substream to the parent stream.
+It must be called after done with the substream.
diff --git a/third_party/nanopb/docs/security.rst b/third_party/nanopb/docs/security.rst
new file mode 100644
index 0000000..d854612
--- /dev/null
+++ b/third_party/nanopb/docs/security.rst
@@ -0,0 +1,84 @@
+======================
+Nanopb: Security model
+======================
+
+.. include :: menu.rst
+
+.. contents ::
+
+
+
+Importance of security in a Protocol Buffers library
+====================================================
+In the context of protocol buffers, security comes into play when decoding
+untrusted data. Naturally, if the attacker can modify the contents of a
+protocol buffers message, he can feed the application any values possible.
+Therefore the application itself must be prepared to receive untrusted values.
+
+Where nanopb plays a part is preventing the attacker from running arbitrary
+code on the target system. Mostly this means that there must not be any
+possibility to cause buffer overruns, memory corruption or invalid pointers
+by the means of crafting a malicious message.
+
+Division of trusted and untrusted data
+======================================
+The following data is regarded as **trusted**. It must be under the control of
+the application writer. Malicious data in these structures could cause
+security issues, such as execution of arbitrary code:
+
+1. Callback, pointer and extension fields in message structures given to
+   pb_encode() and pb_decode(). These fields are memory pointers, and are
+   generated depending on the message definition in the .proto file.
+2. The automatically generated field definitions, i.e. *pb_field_t* lists.
+3. Contents of the *pb_istream_t* and *pb_ostream_t* structures (this does not
+   mean the contents of the stream itself, just the stream definition).
+
+The following data is regarded as **untrusted**. Invalid/malicious data in
+these will cause "garbage in, garbage out" behaviour. It will not cause
+buffer overflows, information disclosure or other security problems:
+
+1. All data read from *pb_istream_t*.
+2. All fields in message structures, except:
+   
+   - callbacks (*pb_callback_t* structures)
+   - pointer fields (malloc support) and *_count* fields for pointers
+   - extensions (*pb_extension_t* structures)
+
+Invariants
+==========
+The following invariants are maintained during operation, even if the
+untrusted data has been maliciously crafted:
+
+1. Nanopb will never read more than *bytes_left* bytes from *pb_istream_t*.
+2. Nanopb will never write more than *max_size* bytes to *pb_ostream_t*.
+3. Nanopb will never access memory out of bounds of the message structure.
+4. After pb_decode() returns successfully, the message structure will be
+   internally consistent:
+
+   - The *count* fields of arrays will not exceed the array size.
+   - The *size* field of bytes will not exceed the allocated size.
+   - All string fields will have null terminator.
+
+5. After pb_encode() returns successfully, the resulting message is a valid
+   protocol buffers message. (Except if user-defined callbacks write incorrect
+   data.)
+
+Further considerations
+======================
+Even if the nanopb library is free of any security issues, there are still
+several possible attack vectors that the application author must consider.
+The following list is not comprehensive:
+
+1. Stack usage may depend on the contents of the message. The message
+   definition places an upper bound on how much stack will be used. Tests
+   should be run with all fields present, to record the maximum possible
+   stack usage.
+2. Callbacks can do anything. The code for the callbacks must be carefully
+   checked if they are used with untrusted data.
+3. If using stream input, a maximum size should be set in *pb_istream_t* to
+   stop a denial of service attack from using an infinite message.
+4. If using network sockets as streams, a timeout should be set to stop
+   denial of service attacks.
+5. If using *malloc()* support, some method of limiting memory use should be
+   employed. This can be done by defining custom *pb_realloc()* function.
+   Nanopb will properly detect and handle failed memory allocations.
diff --git a/third_party/nanopb/examples/cmake_simple/CMakeLists.txt b/third_party/nanopb/examples/cmake_simple/CMakeLists.txt
new file mode 100644
index 0000000..e5f33a0
--- /dev/null
+++ b/third_party/nanopb/examples/cmake_simple/CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 2.8)
+project(NANOPB_CMAKE_SIMPLE C)
+
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../extra)
+find_package(Nanopb REQUIRED)
+include_directories(${NANOPB_INCLUDE_DIRS})
+
+nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS simple.proto)
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+#add_custom_target(generate_proto_sources DEPENDS ${PROTO_SRCS} ${PROTO_HDRS})
+set_source_files_properties(${PROTO_SRCS} ${PROTO_HDRS}
+    PROPERTIES GENERATED TRUE)
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -g -O0")
+
+add_executable(simple simple.c ${PROTO_SRCS} ${PROTO_HDRS})
diff --git a/third_party/nanopb/examples/cmake_simple/README.txt b/third_party/nanopb/examples/cmake_simple/README.txt
new file mode 100644
index 0000000..aa0f3f3
--- /dev/null
+++ b/third_party/nanopb/examples/cmake_simple/README.txt
@@ -0,0 +1,18 @@
+Nanopb example "simple" using CMake
+=======================
+
+This example is the same as the simple nanopb example but built using CMake.
+
+Example usage
+-------------
+
+On Linux, create a build directory and then call cmake:
+
+    nanopb/examples/cmake_simple$ mkdir build
+    nanopb/examples/cmake_simple$ cd build/
+    nanopb/examples/cmake_simple/build$ cmake ..
+    nanopb/examples/cmake_simple/build$ make
+
+After that, you can run it with the command: ./simple
+
+On other platforms supported by CMake, refer to CMake instructions.
diff --git a/third_party/nanopb/examples/cmake_simple/simple.c b/third_party/nanopb/examples/cmake_simple/simple.c
new file mode 100644
index 0000000..1f6b137
--- /dev/null
+++ b/third_party/nanopb/examples/cmake_simple/simple.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+#include "simple.pb.h"
+
+int main()
+{
+    /* This is the buffer where we will store our message. */
+    uint8_t buffer[128];
+    size_t message_length;
+    bool status;
+    
+    /* Encode our message */
+    {
+        /* Allocate space on the stack to store the message data.
+         *
+         * Nanopb generates simple struct definitions for all the messages.
+         * - check out the contents of simple.pb.h!
+         * It is a good idea to always initialize your structures
+         * so that you do not have garbage data from RAM in there.
+         */
+        SimpleMessage message = SimpleMessage_init_zero;
+        
+        /* Create a stream that will write to our buffer. */
+        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+        
+        /* Fill in the lucky number */
+        message.lucky_number = 13;
+        
+        /* Now we are ready to encode the message! */
+        status = pb_encode(&stream, SimpleMessage_fields, &message);
+        message_length = stream.bytes_written;
+        
+        /* Then just check for any errors.. */
+        if (!status)
+        {
+            printf("Encoding failed: %s\n", PB_GET_ERROR(&stream));
+            return 1;
+        }
+    }
+    
+    /* Now we could transmit the message over network, store it in a file or
+     * wrap it to a pigeon's leg.
+     */
+
+    /* But because we are lazy, we will just decode it immediately. */
+    
+    {
+        /* Allocate space for the decoded message. */
+        SimpleMessage message = SimpleMessage_init_zero;
+        
+        /* Create a stream that reads from the buffer. */
+        pb_istream_t stream = pb_istream_from_buffer(buffer, message_length);
+        
+        /* Now we are ready to decode the message. */
+        status = pb_decode(&stream, SimpleMessage_fields, &message);
+        
+        /* Check for errors... */
+        if (!status)
+        {
+            printf("Decoding failed: %s\n", PB_GET_ERROR(&stream));
+            return 1;
+        }
+        
+        /* Print the data contained in the message. */
+        printf("Your lucky number was %d!\n", message.lucky_number);
+    }
+    
+    return 0;
+}
+
diff --git a/third_party/nanopb/examples/cmake_simple/simple.proto b/third_party/nanopb/examples/cmake_simple/simple.proto
new file mode 100644
index 0000000..5c73a3b
--- /dev/null
+++ b/third_party/nanopb/examples/cmake_simple/simple.proto
@@ -0,0 +1,9 @@
+// A very simple protocol definition, consisting of only
+// one message.
+
+syntax = "proto2";
+
+message SimpleMessage {
+    required int32 lucky_number = 1;
+}
+
diff --git a/third_party/nanopb/examples/network_server/Makefile b/third_party/nanopb/examples/network_server/Makefile
new file mode 100644
index 0000000..2c7639a
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/Makefile
@@ -0,0 +1,17 @@
+# Include the nanopb provided Makefile rules
+include ../../extra/nanopb.mk
+
+# Compiler flags to enable all warnings & debug info
+CFLAGS = -ansi -Wall -Werror -g -O0
+CFLAGS += -I$(NANOPB_DIR)
+
+all: server client
+
+.SUFFIXES:
+
+clean:
+	rm -f server client fileproto.pb.c fileproto.pb.h
+
+%: %.c common.c fileproto.pb.c
+	$(CC) $(CFLAGS) -o $@ $^ $(NANOPB_CORE)
+
diff --git a/third_party/nanopb/examples/network_server/README.txt b/third_party/nanopb/examples/network_server/README.txt
new file mode 100644
index 0000000..7bdcbed
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/README.txt
@@ -0,0 +1,60 @@
+Nanopb example "network_server"
+===============================
+
+This example demonstrates the use of nanopb to communicate over network
+connections. It consists of a server that sends file listings, and of
+a client that requests the file list from the server.
+
+Example usage
+-------------
+
+user@host:~/nanopb/examples/network_server$ make        # Build the example
+protoc -ofileproto.pb fileproto.proto
+python ../../generator/nanopb_generator.py fileproto.pb
+Writing to fileproto.pb.h and fileproto.pb.c
+cc -ansi -Wall -Werror -I .. -g -O0 -I../.. -o server server.c
+    ../../pb_decode.c ../../pb_encode.c fileproto.pb.c common.c
+cc -ansi -Wall -Werror -I .. -g -O0 -I../.. -o client client.c
+    ../../pb_decode.c ../../pb_encode.c fileproto.pb.c common.c
+
+user@host:~/nanopb/examples/network_server$ ./server &  # Start the server on background
+[1] 24462
+
+petteri@oddish:~/nanopb/examples/network_server$ ./client /bin  # Request the server to list /bin
+Got connection.
+Listing directory: /bin
+1327119    bzdiff
+1327126    bzless
+1327147    ps
+1327178    ntfsmove
+1327271    mv
+1327187    mount
+1327259    false
+1327266    tempfile
+1327285    zfgrep
+1327165    gzexe
+1327204    nc.openbsd
+1327260    uname
+
+
+Details of implementation
+-------------------------
+fileproto.proto contains the portable Google Protocol Buffers protocol definition.
+It could be used as-is to implement a server or a client in any other language, for
+example Python or Java.
+
+fileproto.options contains the nanopb-specific options for the protocol file. This
+sets the amount of space allocated for file names when decoding messages.
+
+common.c/h contains functions that allow nanopb to read and write directly from
+network socket. This way there is no need to allocate a separate buffer to store
+the message.
+
+server.c contains the code to open a listening socket, to respond to clients and
+to list directory contents.
+
+client.c contains the code to connect to a server, to send a request and to print
+the response message.
+
+The code is implemented using the POSIX socket api, but it should be easy enough
+to port into any other socket api, such as lwip.
diff --git a/third_party/nanopb/examples/network_server/client.c b/third_party/nanopb/examples/network_server/client.c
new file mode 100644
index 0000000..00f6dab
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/client.c
@@ -0,0 +1,142 @@
+/* This is a simple TCP client that connects to port 1234 and prints a list
+ * of files in a given directory.
+ *
+ * It directly deserializes and serializes messages from network, minimizing
+ * memory use.
+ * 
+ * For flexibility, this example is implemented using posix api.
+ * In a real embedded system you would typically use some other kind of
+ * a communication and filesystem layer.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pb_encode.h>
+#include <pb_decode.h>
+
+#include "fileproto.pb.h"
+#include "common.h"
+
+/* This callback function will be called once for each filename received
+ * from the server. The filenames will be printed out immediately, so that
+ * no memory has to be allocated for them.
+ */
+bool printfile_callback(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    FileInfo fileinfo = {};
+    
+    if (!pb_decode(stream, FileInfo_fields, &fileinfo))
+        return false;
+    
+    printf("%-10lld %s\n", (long long)fileinfo.inode, fileinfo.name);
+    
+    return true;
+}
+
+/* This function sends a request to socket 'fd' to list the files in
+ * directory given in 'path'. The results received from server will
+ * be printed to stdout.
+ */
+bool listdir(int fd, char *path)
+{
+    /* Construct and send the request to server */
+    {
+        ListFilesRequest request = {};
+        pb_ostream_t output = pb_ostream_from_socket(fd);
+        uint8_t zero = 0;
+        
+        /* In our protocol, path is optional. If it is not given,
+         * the server will list the root directory. */
+        if (path == NULL)
+        {
+            request.has_path = false;
+        }
+        else
+        {
+            request.has_path = true;
+            if (strlen(path) + 1 > sizeof(request.path))
+            {
+                fprintf(stderr, "Too long path.\n");
+                return false;
+            }
+            
+            strcpy(request.path, path);
+        }
+        
+        /* Encode the request. It is written to the socket immediately
+         * through our custom stream. */
+        if (!pb_encode(&output, ListFilesRequest_fields, &request))
+        {
+            fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&output));
+            return false;
+        }
+        
+        /* We signal the end of request with a 0 tag. */
+        pb_write(&output, &zero, 1);
+    }
+    
+    /* Read back the response from server */
+    {
+        ListFilesResponse response = {};
+        pb_istream_t input = pb_istream_from_socket(fd);
+        
+        /* Give a pointer to our callback function, which will handle the
+         * filenames as they arrive. */
+        response.file.funcs.decode = &printfile_callback;
+        
+        if (!pb_decode(&input, ListFilesResponse_fields, &response))
+        {
+            fprintf(stderr, "Decode failed: %s\n", PB_GET_ERROR(&input));
+            return false;
+        }
+        
+        /* If the message from server decodes properly, but directory was
+         * not found on server side, we get path_error == true. */
+        if (response.path_error)
+        {
+            fprintf(stderr, "Server reported error.\n");
+            return false;
+        }
+    }
+    
+    return true;
+}
+
+int main(int argc, char **argv)
+{
+    int sockfd;
+    struct sockaddr_in servaddr;
+    char *path = NULL;
+    
+    if (argc > 1)
+        path = argv[1];
+    
+    sockfd = socket(AF_INET, SOCK_STREAM, 0);
+    
+    /* Connect to server running on localhost:1234 */
+    memset(&servaddr, 0, sizeof(servaddr));
+    servaddr.sin_family = AF_INET;
+    servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    servaddr.sin_port = htons(1234);
+    
+    if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0)
+    {
+        perror("connect");
+        return 1;
+    }
+    
+    /* Send the directory listing request */
+    if (!listdir(sockfd, path))
+        return 2;
+    
+    /* Close connection */
+    close(sockfd);
+    
+    return 0;
+}
diff --git a/third_party/nanopb/examples/network_server/common.c b/third_party/nanopb/examples/network_server/common.c
new file mode 100644
index 0000000..04a5aa8
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/common.c
@@ -0,0 +1,40 @@
+/* Simple binding of nanopb streams to TCP sockets.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+
+#include "common.h"
+
+static bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
+{
+    int fd = (intptr_t)stream->state;
+    return send(fd, buf, count, 0) == count;
+}
+
+static bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
+{
+    int fd = (intptr_t)stream->state;
+    int result;
+    
+    result = recv(fd, buf, count, MSG_WAITALL);
+    
+    if (result == 0)
+        stream->bytes_left = 0; /* EOF */
+    
+    return result == count;
+}
+
+pb_ostream_t pb_ostream_from_socket(int fd)
+{
+    pb_ostream_t stream = {&write_callback, (void*)(intptr_t)fd, SIZE_MAX, 0};
+    return stream;
+}
+
+pb_istream_t pb_istream_from_socket(int fd)
+{
+    pb_istream_t stream = {&read_callback, (void*)(intptr_t)fd, SIZE_MAX};
+    return stream;
+}
diff --git a/third_party/nanopb/examples/network_server/common.h b/third_party/nanopb/examples/network_server/common.h
new file mode 100644
index 0000000..8dab3b7
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/common.h
@@ -0,0 +1,9 @@
+#ifndef _PB_EXAMPLE_COMMON_H_
+#define _PB_EXAMPLE_COMMON_H_
+
+#include <pb.h>
+
+pb_ostream_t pb_ostream_from_socket(int fd);
+pb_istream_t pb_istream_from_socket(int fd);
+
+#endif
\ No newline at end of file
diff --git a/third_party/nanopb/examples/network_server/fileproto.options b/third_party/nanopb/examples/network_server/fileproto.options
new file mode 100644
index 0000000..29a2ab0
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/fileproto.options
@@ -0,0 +1,13 @@
+# This file defines the nanopb-specific options for the messages defined
+# in fileproto.proto.
+#
+# If you come from high-level programming background, the hardcoded
+# maximum lengths may disgust you. However, if your microcontroller only
+# has a few kB of ram to begin with, setting reasonable limits for
+# filenames is ok.
+#
+# On the other hand, using the callback interface, it is not necessary
+# to set a limit on the number of files in the response.
+
+ListFilesRequest.path 	max_size:128
+FileInfo.name 		max_size:128
diff --git a/third_party/nanopb/examples/network_server/fileproto.proto b/third_party/nanopb/examples/network_server/fileproto.proto
new file mode 100644
index 0000000..5640b8d
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/fileproto.proto
@@ -0,0 +1,20 @@
+// This defines protocol for a simple server that lists files.
+//
+// See also the nanopb-specific options in fileproto.options.
+
+syntax = "proto2";
+
+message ListFilesRequest {
+    optional string path = 1 [default = "/"];
+}
+
+message FileInfo {
+    required uint64 inode = 1;
+    required string name = 2;
+}
+
+message ListFilesResponse {
+    optional bool path_error = 1 [default = false];
+    repeated FileInfo file = 2;
+}
+
diff --git a/third_party/nanopb/examples/network_server/server.c b/third_party/nanopb/examples/network_server/server.c
new file mode 100644
index 0000000..46a5f38
--- /dev/null
+++ b/third_party/nanopb/examples/network_server/server.c
@@ -0,0 +1,158 @@
+/* This is a simple TCP server that listens on port 1234 and provides lists
+ * of files to clients, using a protocol defined in file_server.proto.
+ *
+ * It directly deserializes and serializes messages from network, minimizing
+ * memory use.
+ * 
+ * For flexibility, this example is implemented using posix api.
+ * In a real embedded system you would typically use some other kind of
+ * a communication and filesystem layer.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pb_encode.h>
+#include <pb_decode.h>
+
+#include "fileproto.pb.h"
+#include "common.h"
+
+/* This callback function will be called once during the encoding.
+ * It will write out any number of FileInfo entries, without consuming unnecessary memory.
+ * This is accomplished by fetching the filenames one at a time and encoding them
+ * immediately.
+ */
+bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    DIR *dir = (DIR*) *arg;
+    struct dirent *file;
+    FileInfo fileinfo = {};
+    
+    while ((file = readdir(dir)) != NULL)
+    {
+        fileinfo.inode = file->d_ino;
+        strncpy(fileinfo.name, file->d_name, sizeof(fileinfo.name));
+        fileinfo.name[sizeof(fileinfo.name) - 1] = '\0';
+        
+        /* This encodes the header for the field, based on the constant info
+         * from pb_field_t. */
+        if (!pb_encode_tag_for_field(stream, field))
+            return false;
+        
+        /* This encodes the data for the field, based on our FileInfo structure. */
+        if (!pb_encode_submessage(stream, FileInfo_fields, &fileinfo))
+            return false;
+    }
+    
+    return true;
+}
+
+/* Handle one arriving client connection.
+ * Clients are expected to send a ListFilesRequest, terminated by a '0'.
+ * Server will respond with a ListFilesResponse message.
+ */
+void handle_connection(int connfd)
+{
+    DIR *directory = NULL;
+    
+    /* Decode the message from the client and open the requested directory. */
+    {
+        ListFilesRequest request = {};
+        pb_istream_t input = pb_istream_from_socket(connfd);
+        
+        if (!pb_decode(&input, ListFilesRequest_fields, &request))
+        {
+            printf("Decode failed: %s\n", PB_GET_ERROR(&input));
+            return;
+        }
+        
+        directory = opendir(request.path);
+        printf("Listing directory: %s\n", request.path);
+    }
+    
+    /* List the files in the directory and transmit the response to client */
+    {
+        ListFilesResponse response = {};
+        pb_ostream_t output = pb_ostream_from_socket(connfd);
+        
+        if (directory == NULL)
+        {
+            perror("opendir");
+            
+            /* Directory was not found, transmit error status */
+            response.has_path_error = true;
+            response.path_error = true;
+            response.file.funcs.encode = NULL;
+        }
+        else
+        {
+            /* Directory was found, transmit filenames */
+            response.has_path_error = false;
+            response.file.funcs.encode = &listdir_callback;
+            response.file.arg = directory;
+        }
+        
+        if (!pb_encode(&output, ListFilesResponse_fields, &response))
+        {
+            printf("Encoding failed: %s\n", PB_GET_ERROR(&output));
+        }
+    }
+    
+    if (directory != NULL)
+        closedir(directory);
+}
+
+int main(int argc, char **argv)
+{
+    int listenfd, connfd;
+    struct sockaddr_in servaddr;
+    int reuse = 1;
+    
+    /* Listen on localhost:1234 for TCP connections */
+    listenfd = socket(AF_INET, SOCK_STREAM, 0);
+    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
+    
+    memset(&servaddr, 0, sizeof(servaddr));
+    servaddr.sin_family = AF_INET;
+    servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    servaddr.sin_port = htons(1234);
+    if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0)
+    {
+        perror("bind");
+        return 1;
+    }
+    
+    if (listen(listenfd, 5) != 0)
+    {
+        perror("listen");
+        return 1;
+    }
+    
+    for(;;)
+    {
+        /* Wait for a client */
+        connfd = accept(listenfd, NULL, NULL);
+        
+        if (connfd < 0)
+        {
+            perror("accept");
+            return 1;
+        }
+        
+        printf("Got connection.\n");
+        
+        handle_connection(connfd);
+        
+        printf("Closing connection.\n");
+        
+        close(connfd);
+    }
+    
+    return 0;
+}
diff --git a/third_party/nanopb/examples/simple/Makefile b/third_party/nanopb/examples/simple/Makefile
new file mode 100644
index 0000000..970a865
--- /dev/null
+++ b/third_party/nanopb/examples/simple/Makefile
@@ -0,0 +1,22 @@
+# Include the nanopb provided Makefile rules
+include ../../extra/nanopb.mk
+
+# Compiler flags to enable all warnings & debug info
+CFLAGS = -Wall -Werror -g -O0
+CFLAGS += -I$(NANOPB_DIR)
+
+# C source code files that are required
+CSRC  = simple.c                   # The main program
+CSRC += simple.pb.c                # The compiled protocol definition
+CSRC += $(NANOPB_DIR)/pb_encode.c  # The nanopb encoder
+CSRC += $(NANOPB_DIR)/pb_decode.c  # The nanopb decoder
+CSRC += $(NANOPB_DIR)/pb_common.c  # The nanopb common parts
+
+# Build rule for the main program
+simple: $(CSRC)
+	$(CC) $(CFLAGS) -osimple $(CSRC)
+
+# Build rule for the protocol
+simple.pb.c: simple.proto
+	$(PROTOC) $(PROTOC_OPTS) --nanopb_out=. simple.proto
+
diff --git a/third_party/nanopb/examples/simple/README.txt b/third_party/nanopb/examples/simple/README.txt
new file mode 100644
index 0000000..ee77bfc
--- /dev/null
+++ b/third_party/nanopb/examples/simple/README.txt
@@ -0,0 +1,29 @@
+Nanopb example "simple"
+=======================
+
+This example demonstrates the very basic use of nanopb. It encodes and
+decodes a simple message.
+
+The code uses four different API functions:
+
+  * pb_ostream_from_buffer() to declare the output buffer that is to be used
+  * pb_encode() to encode a message
+  * pb_istream_from_buffer() to declare the input buffer that is to be used
+  * pb_decode() to decode a message
+
+Example usage
+-------------
+
+On Linux, simply type "make" to build the example. After that, you can
+run it with the command: ./simple
+
+On other platforms, you first have to compile the protocol definition using
+the following command::
+
+  ../../generator-bin/protoc --nanopb_out=. simple.proto
+
+After that, add the following four files to your project and compile:
+
+  simple.c  simple.pb.c  pb_encode.c  pb_decode.c
+
+
diff --git a/third_party/nanopb/examples/simple/simple.c b/third_party/nanopb/examples/simple/simple.c
new file mode 100644
index 0000000..1f6b137
--- /dev/null
+++ b/third_party/nanopb/examples/simple/simple.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+#include "simple.pb.h"
+
+int main()
+{
+    /* This is the buffer where we will store our message. */
+    uint8_t buffer[128];
+    size_t message_length;
+    bool status;
+    
+    /* Encode our message */
+    {
+        /* Allocate space on the stack to store the message data.
+         *
+         * Nanopb generates simple struct definitions for all the messages.
+         * - check out the contents of simple.pb.h!
+         * It is a good idea to always initialize your structures
+         * so that you do not have garbage data from RAM in there.
+         */
+        SimpleMessage message = SimpleMessage_init_zero;
+        
+        /* Create a stream that will write to our buffer. */
+        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+        
+        /* Fill in the lucky number */
+        message.lucky_number = 13;
+        
+        /* Now we are ready to encode the message! */
+        status = pb_encode(&stream, SimpleMessage_fields, &message);
+        message_length = stream.bytes_written;
+        
+        /* Then just check for any errors.. */
+        if (!status)
+        {
+            printf("Encoding failed: %s\n", PB_GET_ERROR(&stream));
+            return 1;
+        }
+    }
+    
+    /* Now we could transmit the message over network, store it in a file or
+     * wrap it to a pigeon's leg.
+     */
+
+    /* But because we are lazy, we will just decode it immediately. */
+    
+    {
+        /* Allocate space for the decoded message. */
+        SimpleMessage message = SimpleMessage_init_zero;
+        
+        /* Create a stream that reads from the buffer. */
+        pb_istream_t stream = pb_istream_from_buffer(buffer, message_length);
+        
+        /* Now we are ready to decode the message. */
+        status = pb_decode(&stream, SimpleMessage_fields, &message);
+        
+        /* Check for errors... */
+        if (!status)
+        {
+            printf("Decoding failed: %s\n", PB_GET_ERROR(&stream));
+            return 1;
+        }
+        
+        /* Print the data contained in the message. */
+        printf("Your lucky number was %d!\n", message.lucky_number);
+    }
+    
+    return 0;
+}
+
diff --git a/third_party/nanopb/examples/simple/simple.proto b/third_party/nanopb/examples/simple/simple.proto
new file mode 100644
index 0000000..5c73a3b
--- /dev/null
+++ b/third_party/nanopb/examples/simple/simple.proto
@@ -0,0 +1,9 @@
+// A very simple protocol definition, consisting of only
+// one message.
+
+syntax = "proto2";
+
+message SimpleMessage {
+    required int32 lucky_number = 1;
+}
+
diff --git a/third_party/nanopb/examples/using_double_on_avr/Makefile b/third_party/nanopb/examples/using_double_on_avr/Makefile
new file mode 100644
index 0000000..874a64b
--- /dev/null
+++ b/third_party/nanopb/examples/using_double_on_avr/Makefile
@@ -0,0 +1,24 @@
+# Include the nanopb provided Makefile rules
+include ../../extra/nanopb.mk
+
+# Compiler flags to enable all warnings & debug info
+CFLAGS = -Wall -Werror -g -O0
+CFLAGS += -I$(NANOPB_DIR)
+
+all: run_tests
+
+.SUFFIXES:
+
+clean:
+	rm -f test_conversions encode_double decode_double doubleproto.pb.c doubleproto.pb.h
+
+test_conversions: test_conversions.c double_conversion.c
+	$(CC) $(CFLAGS) -o $@ $^
+
+%: %.c double_conversion.c doubleproto.pb.c
+	$(CC) $(CFLAGS) -o $@ $^ $(NANOPB_CORE)
+
+run_tests: test_conversions encode_double decode_double
+	./test_conversions
+	./encode_double | ./decode_double
+    
diff --git a/third_party/nanopb/examples/using_double_on_avr/README.txt b/third_party/nanopb/examples/using_double_on_avr/README.txt
new file mode 100644
index 0000000..d9fcdfc
--- /dev/null
+++ b/third_party/nanopb/examples/using_double_on_avr/README.txt
@@ -0,0 +1,25 @@
+Nanopb example "using_double_on_avr"
+====================================
+
+Some processors/compilers, such as AVR-GCC, do not support the double
+datatype. Instead, they have sizeof(double) == 4. Because protocol
+binary format uses the double encoding directly, this causes trouble
+if the protocol in .proto requires double fields.
+
+This directory contains a solution to this problem. It uses uint64_t
+to store the raw wire values, because its size is correct on all
+platforms. The file double_conversion.c provides functions that
+convert these values to/from floats, without relying on compiler
+support.
+
+To use this method, you need to make some modifications to your code:
+
+1) Change all 'double' fields into 'fixed64' in the .proto.
+
+2) Whenever writing to a 'double' field, use float_to_double().
+
+3) Whenever reading a 'double' field, use double_to_float().
+
+The conversion routines are as accurate as the float datatype can
+be. Furthermore, they should handle all special values (NaN, inf, denormalized
+numbers) correctly. There are testcases in test_conversions.c.
diff --git a/third_party/nanopb/examples/using_double_on_avr/decode_double.c b/third_party/nanopb/examples/using_double_on_avr/decode_double.c
new file mode 100644
index 0000000..5802eca
--- /dev/null
+++ b/third_party/nanopb/examples/using_double_on_avr/decode_double.c
@@ -0,0 +1,33 @@
+/* Decodes a double value into a float variable.
+ * Used to read double values with AVR code, which doesn't support double directly.
+ */
+
+#include <stdio.h>
+#include <pb_decode.h>
+#include "double_conversion.h"
+#include "doubleproto.pb.h"
+
+int main()
+{
+    uint8_t buffer[32];
+    size_t count = fread(buffer, 1, sizeof(buffer), stdin);
+    pb_istream_t stream = pb_istream_from_buffer(buffer, count);
+    
+    AVRDoubleMessage message;
+    pb_decode(&stream, AVRDoubleMessage_fields, &message);
+    
+    float v1 = double_to_float(message.field1);
+    float v2 = double_to_float(message.field2);
+
+    printf("Values: %f %f\n", v1, v2);
+    
+    if (v1 == 1234.5678f &&
+        v2 == 0.00001f)
+    {
+        return 0;
+    }
+    else
+    {
+        return 1;
+    }
+}
diff --git a/third_party/nanopb/examples/using_double_on_avr/double_conversion.c b/third_party/nanopb/examples/using_double_on_avr/double_conversion.c
new file mode 100644
index 0000000..cf79b9a
--- /dev/null
+++ b/third_party/nanopb/examples/using_double_on_avr/double_conversion.c
@@ -0,0 +1,123 @@
+/* Conversion routines for platforms that do not support 'double' directly. */
+
+#include "double_conversion.h"
+#include <math.h>
+
+typedef union {
+    float f;
+    uint32_t i;
+} conversion_t;
+
+/* Note: IEE 754 standard specifies float formats as follows:
+ * Single precision: sign,  8-bit exp, 23-bit frac.
+ * Double precision: sign, 11-bit exp, 52-bit frac.
+ */
+
+uint64_t float_to_double(float value)
+{
+    conversion_t in;
+    in.f = value;
+    uint8_t sign;
+    int16_t exponent;
+    uint64_t mantissa;
+    
+    /* Decompose input value */
+    sign = (in.i >> 31) & 1;
+    exponent = ((in.i >> 23) & 0xFF) - 127;
+    mantissa = in.i & 0x7FFFFF;
+    
+    if (exponent == 128)
+    {
+        /* Special value (NaN etc.) */
+        exponent = 1024;
+    }
+    else if (exponent == -127)
+    {
+        if (!mantissa)
+        {
+            /* Zero */
+            exponent = -1023;
+        }
+        else
+        {
+            /* Denormalized */
+            mantissa <<= 1;
+            while (!(mantissa & 0x800000))
+            {
+                mantissa <<= 1;
+                exponent--;
+            }
+            mantissa &= 0x7FFFFF;
+        }
+    }
+    
+    /* Combine fields */
+    mantissa <<= 29;
+    mantissa |= (uint64_t)(exponent + 1023) << 52;
+    mantissa |= (uint64_t)sign << 63;
+    
+    return mantissa;
+}
+
+float double_to_float(uint64_t value)
+{
+    uint8_t sign;
+    int16_t exponent;
+    uint32_t mantissa;
+    conversion_t out;
+
+    /* Decompose input value */
+    sign = (value >> 63) & 1;
+    exponent = ((value >> 52) & 0x7FF) - 1023;
+    mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */
+ 
+    /* Figure if value is in range representable by floats. */
+    if (exponent == 1024)
+    {
+        /* Special value */
+        exponent = 128;
+    }
+    else if (exponent > 127)
+    {
+        /* Too large */        
+        if (sign)
+            return -INFINITY;
+        else
+            return INFINITY;
+    }
+    else if (exponent < -150)
+    {
+        /* Too small */
+        if (sign)
+            return -0.0f;
+        else
+            return 0.0f;
+    }
+    else if (exponent < -126)
+    {
+        /* Denormalized */
+        mantissa |= 0x1000000;
+        mantissa >>= (-126 - exponent);
+        exponent = -127;
+    }
+ 
+    /* Round off mantissa */
+    mantissa = (mantissa + 1) >> 1;
+    
+    /* Check if mantissa went over 2.0 */
+    if (mantissa & 0x800000)
+    {
+        exponent += 1;
+        mantissa &= 0x7FFFFF;
+        mantissa >>= 1;
+    }
+    
+    /* Combine fields */
+    out.i = mantissa;
+    out.i |= (uint32_t)(exponent + 127) << 23;
+    out.i |= (uint32_t)sign << 31;
+    
+    return out.f;
+}
+
+
diff --git a/third_party/nanopb/examples/using_double_on_avr/double_conversion.h b/third_party/nanopb/examples/using_double_on_avr/double_conversion.h
new file mode 100644
index 0000000..62b6a8a
--- /dev/null
+++ b/third_party/nanopb/examples/using_double_on_avr/double_conversion.h
@@ -0,0 +1,26 @@
+/* AVR-GCC does not have real double datatype. Instead its double
+ * is equal to float, i.e. 32 bit value. If you need to communicate
+ * with other systems that use double in their .proto files, you
+ * need to do some conversion.
+ *
+ * These functions use bitwise operations to mangle floats into doubles
+ * and then store them in uint64_t datatype.
+ */
+
+#ifndef DOUBLE_CONVERSION
+#define DOUBLE_CONVERSION
+
+#include <stdint.h>
+
+/* Convert native 4-byte float into a 8-byte double. */
+extern uint64_t float_to_double(float value);
+
+/* Convert 8-byte double into native 4-byte float.
+ * Values are rounded to nearest, 0.5 away from zero.
+ * Overflowing values are converted to Inf or -Inf.
+ */
+extern float double_to_float(uint64_t value);
+
+
+#endif
+
diff --git a/third_party/nanopb/examples/using_double_on_avr/doubleproto.proto b/third_party/nanopb/examples/using_double_on_avr/doubleproto.proto
new file mode 100644
index 0000000..72d3f9c
--- /dev/null
+++ b/third_party/nanopb/examples/using_double_on_avr/doubleproto.proto
@@ -0,0 +1,15 @@
+// A message containing doubles, as used by other applications.
+syntax = "proto2";
+
+message DoubleMessage {
+    required double field1 = 1;
+    required double field2 = 2;
+}
+
+// A message containing doubles, but redefined using uint64_t.
+// For use in AVR code.
+message AVRDoubleMessage {
+    required fixed64 field1 = 1;
+    required fixed64 field2 = 2;
+}
+
diff --git a/third_party/nanopb/examples/using_double_on_avr/encode_double.c b/third_party/nanopb/examples/using_double_on_avr/encode_double.c
new file mode 100644
index 0000000..cd532d4
--- /dev/null
+++ b/third_party/nanopb/examples/using_double_on_avr/encode_double.c
@@ -0,0 +1,25 @@
+/* Encodes a float value into a double on the wire.
+ * Used to emit doubles from AVR code, which doesn't support double directly.
+ */
+
+#include <stdio.h>
+#include <pb_encode.h>
+#include "double_conversion.h"
+#include "doubleproto.pb.h"
+
+int main()
+{
+    AVRDoubleMessage message = {
+        float_to_double(1234.5678f),
+        float_to_double(0.00001f)
+    };
+    
+    uint8_t buffer[32];
+    pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+    
+    pb_encode(&stream, AVRDoubleMessage_fields, &message);
+    fwrite(buffer, 1, stream.bytes_written, stdout);
+
+    return 0;
+}
+
diff --git a/third_party/nanopb/examples/using_double_on_avr/test_conversions.c b/third_party/nanopb/examples/using_double_on_avr/test_conversions.c
new file mode 100644
index 0000000..22620a6
--- /dev/null
+++ b/third_party/nanopb/examples/using_double_on_avr/test_conversions.c
@@ -0,0 +1,56 @@
+#include "double_conversion.h"
+#include <math.h>
+#include <stdio.h>
+
+static const double testvalues[] = {
+           0.0,        -0.0,         0.1,         -0.1,
+          M_PI,       -M_PI,  123456.789,  -123456.789,
+      INFINITY,   -INFINITY,         NAN, INFINITY - INFINITY,
+          1e38,       -1e38,        1e39,        -1e39,
+         1e-38,      -1e-38,       1e-39,       -1e-39,
+   3.14159e-37,-3.14159e-37, 3.14159e-43, -3.14159e-43,
+         1e-60,      -1e-60,       1e-45,       -1e-45,
+    0.99999999999999, -0.99999999999999, 127.999999999999, -127.999999999999
+};
+
+#define TESTVALUES_COUNT (sizeof(testvalues)/sizeof(testvalues[0]))
+
+int main()
+{
+    int status = 0;
+    int i;
+    for (i = 0; i < TESTVALUES_COUNT; i++)
+    {
+        double orig = testvalues[i];
+        float expected_float = (float)orig;
+        double expected_double = (double)expected_float;
+        
+        float got_float = double_to_float(*(uint64_t*)&orig);
+        uint64_t got_double = float_to_double(got_float);
+        
+        uint32_t e1 = *(uint32_t*)&expected_float;
+        uint32_t g1 = *(uint32_t*)&got_float;
+        uint64_t e2 = *(uint64_t*)&expected_double;
+        uint64_t g2 = got_double;
+        
+        if (g1 != e1)
+        {
+            printf("%3d double_to_float fail: %08x != %08x\n", i, g1, e1);
+            status = 1;
+        }
+        
+        if (g2 != e2)
+        {
+            printf("%3d float_to_double fail: %016llx != %016llx\n", i,
+                (unsigned long long)g2,
+                (unsigned long long)e2);
+            status = 1;
+        }
+    }
+
+    return status;
+}
+
+    
+    
+
diff --git a/third_party/nanopb/examples/using_union_messages/Makefile b/third_party/nanopb/examples/using_union_messages/Makefile
new file mode 100644
index 0000000..66396a0
--- /dev/null
+++ b/third_party/nanopb/examples/using_union_messages/Makefile
@@ -0,0 +1,20 @@
+# Include the nanopb provided Makefile rules
+include ../../extra/nanopb.mk
+
+# Compiler flags to enable all warnings & debug info
+CFLAGS = -ansi -Wall -Werror -g -O0
+CFLAGS += -I$(NANOPB_DIR)
+
+all: encode decode
+	./encode 1 | ./decode
+	./encode 2 | ./decode
+	./encode 3 | ./decode
+
+.SUFFIXES:
+
+clean:
+	rm -f encode unionproto.pb.h unionproto.pb.c
+
+%: %.c unionproto.pb.c
+	$(CC) $(CFLAGS) -o $@ $^ $(NANOPB_CORE)
+
diff --git a/third_party/nanopb/examples/using_union_messages/README.txt b/third_party/nanopb/examples/using_union_messages/README.txt
new file mode 100644
index 0000000..7a1e75d
--- /dev/null
+++ b/third_party/nanopb/examples/using_union_messages/README.txt
@@ -0,0 +1,52 @@
+Nanopb example "using_union_messages"
+=====================================
+
+Union messages is a common technique in Google Protocol Buffers used to
+represent a group of messages, only one of which is passed at a time.
+It is described in Google's documentation:
+https://developers.google.com/protocol-buffers/docs/techniques#union
+
+This directory contains an example on how to encode and decode union messages
+with minimal memory usage. Usually, nanopb would allocate space to store
+all of the possible messages at the same time, even though at most one of
+them will be used at a time.
+
+By using some of the lower level nanopb APIs, we can manually generate the
+top level message, so that we only need to allocate the one submessage that
+we actually want. Similarly when decoding, we can manually read the tag of
+the top level message, and only then allocate the memory for the submessage
+after we already know its type.
+
+
+Example usage
+-------------
+
+Type `make` to run the example. It will build it and run commands like
+following:
+
+./encode 1 | ./decode
+Got MsgType1: 42
+./encode 2 | ./decode
+Got MsgType2: true
+./encode 3 | ./decode
+Got MsgType3: 3 1415
+
+This simply demonstrates that the "decode" program has correctly identified
+the type of the received message, and managed to decode it.
+
+
+Details of implementation
+-------------------------
+
+unionproto.proto contains the protocol used in the example. It consists of
+three messages: MsgType1, MsgType2 and MsgType3, which are collected together
+into UnionMessage.
+
+encode.c takes one command line argument, which should be a number 1-3. It
+then fills in and encodes the corresponding message, and writes it to stdout.
+
+decode.c reads a UnionMessage from stdin. Then it calls the function
+decode_unionmessage_type() to determine the type of the message. After that,
+the corresponding message is decoded and the contents of it printed to the
+screen.
+
diff --git a/third_party/nanopb/examples/using_union_messages/decode.c b/third_party/nanopb/examples/using_union_messages/decode.c
new file mode 100644
index 0000000..b9f4af5
--- /dev/null
+++ b/third_party/nanopb/examples/using_union_messages/decode.c
@@ -0,0 +1,96 @@
+/* This program reads a message from stdin, detects its type and decodes it.
+ */
+ 
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <pb_decode.h>
+#include "unionproto.pb.h"
+
+/* This function reads manually the first tag from the stream and finds the
+ * corresponding message type. It doesn't yet decode the actual message.
+ *
+ * Returns a pointer to the MsgType_fields array, as an identifier for the
+ * message type. Returns null if the tag is of unknown type or an error occurs.
+ */
+const pb_field_t* decode_unionmessage_type(pb_istream_t *stream)
+{
+    pb_wire_type_t wire_type;
+    uint32_t tag;
+    bool eof;
+
+    while (pb_decode_tag(stream, &wire_type, &tag, &eof))
+    {
+        if (wire_type == PB_WT_STRING)
+        {
+            const pb_field_t *field;
+            for (field = UnionMessage_fields; field->tag != 0; field++)
+            {
+                if (field->tag == tag && (field->type & PB_LTYPE_SUBMESSAGE))
+                {
+                    /* Found our field. */
+                    return field->ptr;
+                }
+            }
+        }
+        
+        /* Wasn't our field.. */
+        pb_skip_field(stream, wire_type);
+    }
+    
+    return NULL;
+}
+
+bool decode_unionmessage_contents(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+    pb_istream_t substream;
+    bool status;
+    if (!pb_make_string_substream(stream, &substream))
+        return false;
+    
+    status = pb_decode(&substream, fields, dest_struct);
+    pb_close_string_substream(stream, &substream);
+    return status;
+}
+
+int main()
+{
+    /* Read the data into buffer */
+    uint8_t buffer[512];
+    size_t count = fread(buffer, 1, sizeof(buffer), stdin);
+    pb_istream_t stream = pb_istream_from_buffer(buffer, count);
+    
+    const pb_field_t *type = decode_unionmessage_type(&stream);
+    bool status = false;
+    
+    if (type == MsgType1_fields)
+    {
+        MsgType1 msg = {};
+        status = decode_unionmessage_contents(&stream, MsgType1_fields, &msg);
+        printf("Got MsgType1: %d\n", msg.value);
+    }
+    else if (type == MsgType2_fields)
+    {
+        MsgType2 msg = {};
+        status = decode_unionmessage_contents(&stream, MsgType2_fields, &msg);
+        printf("Got MsgType2: %s\n", msg.value ? "true" : "false");
+    }
+    else if (type == MsgType3_fields)
+    {
+        MsgType3 msg = {};
+        status = decode_unionmessage_contents(&stream, MsgType3_fields, &msg);
+        printf("Got MsgType3: %d %d\n", msg.value1, msg.value2);    
+    }
+    
+    if (!status)
+    {
+        printf("Decode failed: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    }
+    
+    return 0;
+}
+
+
+
diff --git a/third_party/nanopb/examples/using_union_messages/encode.c b/third_party/nanopb/examples/using_union_messages/encode.c
new file mode 100644
index 0000000..e124bf9
--- /dev/null
+++ b/third_party/nanopb/examples/using_union_messages/encode.c
@@ -0,0 +1,85 @@
+/* This program takes a command line argument and encodes a message in
+ * one of MsgType1, MsgType2 or MsgType3.
+ */
+ 
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <pb_encode.h>
+#include "unionproto.pb.h"
+
+/* This function is the core of the union encoding process. It handles
+ * the top-level pb_field_t array manually, in order to encode a correct
+ * field tag before the message. The pointer to MsgType_fields array is
+ * used as an unique identifier for the message type.
+ */
+bool encode_unionmessage(pb_ostream_t *stream, const pb_field_t messagetype[], const void *message)
+{
+    const pb_field_t *field;
+    for (field = UnionMessage_fields; field->tag != 0; field++)
+    {
+        if (field->ptr == messagetype)
+        {
+            /* This is our field, encode the message using it. */
+            if (!pb_encode_tag_for_field(stream, field))
+                return false;
+            
+            return pb_encode_submessage(stream, messagetype, message);
+        }
+    }
+    
+    /* Didn't find the field for messagetype */
+    return false;
+}
+
+int main(int argc, char **argv)
+{
+    if (argc != 2)
+    {
+        fprintf(stderr, "Usage: %s (1|2|3)\n", argv[0]);
+        return 1;
+    }
+    
+    uint8_t buffer[512];
+    pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+    
+    bool status = false;
+    int msgtype = atoi(argv[1]);
+    if (msgtype == 1)
+    {
+        /* Send message of type 1 */
+        MsgType1 msg = {42};
+        status = encode_unionmessage(&stream, MsgType1_fields, &msg);
+    }
+    else if (msgtype == 2)
+    {
+        /* Send message of type 2 */
+        MsgType2 msg = {true};
+        status = encode_unionmessage(&stream, MsgType2_fields, &msg);
+    }
+    else if (msgtype == 3)
+    {
+        /* Send message of type 3 */
+        MsgType3 msg = {3, 1415};
+        status = encode_unionmessage(&stream, MsgType3_fields, &msg);
+    }
+    else
+    {
+        fprintf(stderr, "Unknown message type: %d\n", msgtype);
+        return 2;
+    }
+    
+    if (!status)
+    {
+        fprintf(stderr, "Encoding failed!\n");
+        return 3;
+    }
+    else
+    {
+        fwrite(buffer, 1, stream.bytes_written, stdout);
+        return 0; /* Success */
+    }
+}
+
+
diff --git a/third_party/nanopb/examples/using_union_messages/unionproto.proto b/third_party/nanopb/examples/using_union_messages/unionproto.proto
new file mode 100644
index 0000000..209df0d
--- /dev/null
+++ b/third_party/nanopb/examples/using_union_messages/unionproto.proto
@@ -0,0 +1,32 @@
+// This is an example of how to handle 'union' style messages
+// with nanopb, without allocating memory for all the message types.
+//
+// There is no official type in Protocol Buffers for describing unions,
+// but they are commonly implemented by filling out exactly one of
+// several optional fields.
+
+syntax = "proto2";
+
+message MsgType1
+{
+    required int32 value = 1;
+}
+
+message MsgType2
+{
+    required bool value = 1;
+}
+
+message MsgType3
+{
+    required int32 value1 = 1;
+    required int32 value2 = 2;
+}
+
+message UnionMessage
+{
+    optional MsgType1 msg1 = 1;
+    optional MsgType2 msg2 = 2;
+    optional MsgType3 msg3 = 3;
+}
+
diff --git a/third_party/nanopb/extra/FindNanopb.cmake b/third_party/nanopb/extra/FindNanopb.cmake
new file mode 100644
index 0000000..9afb21d
--- /dev/null
+++ b/third_party/nanopb/extra/FindNanopb.cmake
@@ -0,0 +1,274 @@
+# This is an example script for use with CMake projects for locating and configuring
+# the nanopb library.
+#
+# The following variables can be set and are optional:
+#
+#
+#   PROTOBUF_SRC_ROOT_FOLDER - When compiling with MSVC, if this cache variable is set
+#                              the protobuf-default VS project build locations
+#                              (vsprojects/Debug & vsprojects/Release) will be searched
+#                              for libraries and binaries.
+#
+#   NANOPB_IMPORT_DIRS       - List of additional directories to be searched for
+#                              imported .proto files.
+#
+#   NANOPB_GENERATE_CPP_APPEND_PATH - By default -I will be passed to protoc
+#                                     for each directory where a proto file is referenced.
+#                                     Set to FALSE if you want to disable this behaviour.
+#
+# Defines the following variables:
+#
+#   NANOPB_FOUND - Found the nanopb library (source&header files, generator tool, protoc compiler tool)
+#   NANOPB_INCLUDE_DIRS - Include directories for Google Protocol Buffers
+#
+# The following cache variables are also available to set or use:
+#   PROTOBUF_PROTOC_EXECUTABLE - The protoc compiler
+#   NANOPB_GENERATOR_SOURCE_DIR - The nanopb generator source
+#
+#  ====================================================================
+#
+# NANOPB_GENERATE_CPP (public function)
+#   SRCS = Variable to define with autogenerated
+#          source files
+#   HDRS = Variable to define with autogenerated
+#          header files
+#   ARGN = proto files
+#
+#  ====================================================================
+#  Example:
+#
+#   set(NANOPB_SRC_ROOT_FOLDER "/path/to/nanopb")
+#   set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${NANOPB_SRC_ROOT_FOLDER}/cmake)
+#   find_package( Nanopb REQUIRED )
+#   include_directories(${NANOPB_INCLUDE_DIRS})
+#
+#   NANOPB_GENERATE_CPP(PROTO_SRCS PROTO_HDRS foo.proto)
+#
+#   include_directories(${CMAKE_CURRENT_BINARY_DIR})
+#   add_executable(bar bar.cc ${PROTO_SRCS} ${PROTO_HDRS})
+#
+#  ====================================================================
+
+#=============================================================================
+# Copyright 2009 Kitware, Inc.
+# Copyright 2009-2011 Philip Lowman <philip@yhbt.com>
+# Copyright 2008 Esben Mose Hansen, Ange Optimization ApS
+#
+# 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 names of Kitware, Inc., the Insight Software Consortium,
+#   nor the names of their 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
+# HOLDER 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.
+#
+#=============================================================================
+#
+# Changes
+# 2013.01.31 - Pavlo Ilin - used Modules/FindProtobuf.cmake from cmake 2.8.10 to
+#                           write FindNanopb.cmake
+#
+#=============================================================================
+
+
+function(NANOPB_GENERATE_CPP SRCS HDRS)
+  if(NOT ARGN)
+    return()
+  endif()
+
+  if(NANOPB_GENERATE_CPP_APPEND_PATH)
+    # Create an include path for each file specified
+    foreach(FIL ${ARGN})
+      get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
+      get_filename_component(ABS_PATH ${ABS_FIL} PATH)
+
+      list(FIND _nanobp_include_path ${ABS_PATH} _contains_already)
+      if(${_contains_already} EQUAL -1)
+          list(APPEND _nanobp_include_path -I ${ABS_PATH})
+      endif()
+    endforeach()
+  else()
+    set(_nanobp_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
+  endif()
+
+  if(DEFINED NANOPB_IMPORT_DIRS)
+    foreach(DIR ${NANOPB_IMPORT_DIRS})
+      get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
+      list(FIND _nanobp_include_path ${ABS_PATH} _contains_already)
+      if(${_contains_already} EQUAL -1)
+          list(APPEND _nanobp_include_path -I ${ABS_PATH})
+      endif()
+    endforeach()
+  endif()
+
+  set(${SRCS})
+  set(${HDRS})
+
+  set(GENERATOR_PATH ${CMAKE_BINARY_DIR}/nanopb/generator)
+
+  set(NANOPB_GENERATOR_EXECUTABLE ${GENERATOR_PATH}/nanopb_generator.py)
+
+  set(GENERATOR_CORE_DIR ${GENERATOR_PATH}/proto)
+  set(GENERATOR_CORE_SRC
+      ${GENERATOR_CORE_DIR}/nanopb.proto
+      ${GENERATOR_CORE_DIR}/plugin.proto)
+
+  # Treat the source diretory as immutable.
+  #
+  # Copy the generator directory to the build directory before
+  # compiling python and proto files.  Fixes issues when using the
+  # same build directory with different python/protobuf versions
+  # as the binary build directory is discarded across builds.
+  #
+  add_custom_command(
+      OUTPUT ${NANOPB_GENERATOR_EXECUTABLE} ${GENERATOR_CORE_SRC}
+      COMMAND ${CMAKE_COMMAND} -E copy_directory
+      ARGS ${NANOPB_GENERATOR_SOURCE_DIR} ${GENERATOR_PATH}
+      VERBATIM)
+
+  set(GENERATOR_CORE_PYTHON_SRC)
+  foreach(FIL ${GENERATOR_CORE_SRC})
+      get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
+      get_filename_component(FIL_WE ${FIL} NAME_WE)
+
+      set(output "${GENERATOR_CORE_DIR}/${FIL_WE}_pb2.py")
+      set(GENERATOR_CORE_PYTHON_SRC ${GENERATOR_CORE_PYTHON_SRC} ${output})
+      add_custom_command(
+        OUTPUT ${output}
+        COMMAND ${PROTOBUF_PROTOC_EXECUTABLE}
+        ARGS -I${GENERATOR_PATH}/proto
+          --python_out=${GENERATOR_CORE_DIR} ${ABS_FIL}
+        DEPENDS ${ABS_FIL}
+        VERBATIM)
+  endforeach()
+
+  foreach(FIL ${ARGN})
+    get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
+    get_filename_component(FIL_WE ${FIL} NAME_WE)
+    get_filename_component(FIL_DIR ${FIL} PATH)
+    set(NANOPB_OPTIONS_FILE ${FIL_DIR}/${FIL_WE}.options)
+    set(NANOPB_OPTIONS)
+    if(EXISTS ${NANOPB_OPTIONS_FILE})
+        set(NANOPB_OPTIONS -f ${NANOPB_OPTIONS_FILE})
+    endif()
+
+    list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.c")
+    list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h")
+
+    add_custom_command(
+      OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb"
+      COMMAND  ${PROTOBUF_PROTOC_EXECUTABLE}
+      ARGS -I${GENERATOR_PATH} -I${GENERATOR_CORE_DIR}
+        -I${CMAKE_CURRENT_BINARY_DIR} ${_nanobp_include_path}
+        -o${FIL_WE}.pb ${ABS_FIL}
+      DEPENDS ${ABS_FIL} ${GENERATOR_CORE_PYTHON_SRC}
+      COMMENT "Running C++ protocol buffer compiler on ${FIL}"
+      VERBATIM )
+
+    add_custom_command(
+      OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.c"
+             "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h"
+      COMMAND ${PYTHON_EXECUTABLE}
+      ARGS ${NANOPB_GENERATOR_EXECUTABLE} ${FIL_WE}.pb ${NANOPB_OPTIONS}
+      DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb"
+      COMMENT "Running nanopb generator on ${FIL_WE}.pb"
+      VERBATIM )
+  endforeach()
+
+  set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE)
+  set(${SRCS} ${${SRCS}} ${NANOPB_SRCS} PARENT_SCOPE)
+  set(${HDRS} ${${HDRS}} ${NANOPB_HDRS} PARENT_SCOPE)
+
+endfunction()
+
+
+
+#
+# Main.
+#
+
+# By default have NANOPB_GENERATE_CPP macro pass -I to protoc
+# for each directory where a proto file is referenced.
+if(NOT DEFINED NANOPB_GENERATE_CPP_APPEND_PATH)
+  set(NANOPB_GENERATE_CPP_APPEND_PATH TRUE)
+endif()
+
+# Make a really good guess regarding location of NANOPB_SRC_ROOT_FOLDER
+if(NOT DEFINED NANOPB_SRC_ROOT_FOLDER)
+  get_filename_component(NANOPB_SRC_ROOT_FOLDER
+                         ${CMAKE_CURRENT_LIST_DIR}/.. ABSOLUTE)
+endif()
+
+# Find the include directory
+find_path(NANOPB_INCLUDE_DIRS
+    pb.h
+    PATHS ${NANOPB_SRC_ROOT_FOLDER}
+)
+mark_as_advanced(NANOPB_INCLUDE_DIRS)
+
+# Find nanopb source files
+set(NANOPB_SRCS)
+set(NANOPB_HDRS)
+list(APPEND _nanopb_srcs pb_decode.c pb_encode.c pb_common.c)
+list(APPEND _nanopb_hdrs pb_decode.h pb_encode.h pb_common.h pb.h)
+
+foreach(FIL ${_nanopb_srcs})
+  find_file(${FIL}__nano_pb_file NAMES ${FIL} PATHS ${NANOPB_SRC_ROOT_FOLDER} ${NANOPB_INCLUDE_DIRS})
+  list(APPEND NANOPB_SRCS "${${FIL}__nano_pb_file}")
+  mark_as_advanced(${FIL}__nano_pb_file)
+endforeach()
+
+foreach(FIL ${_nanopb_hdrs})
+  find_file(${FIL}__nano_pb_file NAMES ${FIL} PATHS ${NANOPB_INCLUDE_DIRS})
+  mark_as_advanced(${FIL}__nano_pb_file)
+  list(APPEND NANOPB_HDRS "${${FIL}__nano_pb_file}")
+endforeach()
+
+# Find the protoc Executable
+find_program(PROTOBUF_PROTOC_EXECUTABLE
+    NAMES protoc
+    DOC "The Google Protocol Buffers Compiler"
+    PATHS
+    ${PROTOBUF_SRC_ROOT_FOLDER}/vsprojects/Release
+    ${PROTOBUF_SRC_ROOT_FOLDER}/vsprojects/Debug
+)
+mark_as_advanced(PROTOBUF_PROTOC_EXECUTABLE)
+
+# Find nanopb generator source dir
+find_path(NANOPB_GENERATOR_SOURCE_DIR
+    NAMES nanopb_generator.py
+    DOC "nanopb generator source"
+    PATHS
+    ${NANOPB_SRC_ROOT_FOLDER}/generator
+)
+mark_as_advanced(NANOPB_GENERATOR_SOURCE_DIR)
+
+find_package(PythonInterp REQUIRED)
+
+include(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(NANOPB DEFAULT_MSG
+  NANOPB_INCLUDE_DIRS
+  NANOPB_SRCS NANOPB_HDRS
+  NANOPB_GENERATOR_SOURCE_DIR
+  PROTOBUF_PROTOC_EXECUTABLE
+  )
diff --git a/third_party/nanopb/extra/nanopb.mk b/third_party/nanopb/extra/nanopb.mk
new file mode 100644
index 0000000..5c2cff5
--- /dev/null
+++ b/third_party/nanopb/extra/nanopb.mk
@@ -0,0 +1,37 @@
+# This is an include file for Makefiles. It provides rules for building
+# .pb.c and .pb.h files out of .proto, as well the path to nanopb core.
+
+# Path to the nanopb root directory
+NANOPB_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))../)
+
+# Files for the nanopb core
+NANOPB_CORE = $(NANOPB_DIR)/pb_encode.c $(NANOPB_DIR)/pb_decode.c $(NANOPB_DIR)/pb_common.c
+
+# Check if we are running on Windows
+ifdef windir
+WINDOWS = 1
+endif
+ifdef WINDIR
+WINDOWS = 1
+endif
+
+# Check whether to use binary version of nanopb_generator or the
+# system-supplied python interpreter.
+ifneq "$(wildcard $(NANOPB_DIR)/generator-bin)" ""
+	# Binary package
+	PROTOC = $(NANOPB_DIR)/generator-bin/protoc
+	PROTOC_OPTS = 
+else
+	# Source only or git checkout
+	PROTOC = protoc
+	ifdef WINDOWS
+		PROTOC_OPTS = --plugin=protoc-gen-nanopb=$(NANOPB_DIR)/generator/protoc-gen-nanopb.bat
+	else
+		PROTOC_OPTS = --plugin=protoc-gen-nanopb=$(NANOPB_DIR)/generator/protoc-gen-nanopb
+	endif
+endif
+
+# Rule for building .pb.c and .pb.h
+%.pb.c %.pb.h: %.proto $(wildcard %.options)
+	$(PROTOC) $(PROTOC_OPTS) --nanopb_out=. $<
+
diff --git a/third_party/nanopb/extra/pb_syshdr.h b/third_party/nanopb/extra/pb_syshdr.h
new file mode 100644
index 0000000..55d06a3
--- /dev/null
+++ b/third_party/nanopb/extra/pb_syshdr.h
@@ -0,0 +1,112 @@
+/* This is an example of a header file for platforms/compilers that do
+ * not come with stdint.h/stddef.h/stdbool.h/string.h. To use it, define
+ * PB_SYSTEM_HEADER as "pb_syshdr.h", including the quotes, and add the
+ * extra folder to your include path.
+ *
+ * It is very likely that you will need to customize this file to suit
+ * your platform. For any compiler that supports C99, this file should
+ * not be necessary.
+ */
+
+#ifndef _PB_SYSHDR_H_
+#define _PB_SYSHDR_H_
+
+/* stdint.h subset */
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#else
+/* You will need to modify these to match the word size of your platform. */
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef signed short int16_t;
+typedef unsigned short uint16_t;
+typedef signed int int32_t;
+typedef unsigned int uint32_t;
+typedef signed long long int64_t;
+typedef unsigned long long uint64_t;
+
+/* These are ok for most platforms, unless uint8_t is actually not available,
+ * in which case you should give the smallest available type. */
+typedef int8_t int_least8_t;
+typedef uint8_t uint_least8_t;
+typedef uint8_t uint_fast8_t;
+typedef int16_t int_least16_t;
+typedef uint16_t uint_least16_t;
+#endif
+
+/* stddef.h subset */
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#else
+
+typedef uint32_t size_t;
+#define offsetof(st, m) ((size_t)(&((st *)0)->m))
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#endif
+
+/* stdbool.h subset */
+#ifdef HAVE_STDBOOL_H
+#include <stdbool.h>
+#else
+
+#ifndef __cplusplus
+typedef int bool;
+#define false 0
+#define true 1
+#endif
+
+#endif
+
+/* stdlib.h subset */
+#ifdef PB_ENABLE_MALLOC
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#else
+void *realloc(void *ptr, size_t size);
+void free(void *ptr);
+#endif
+#endif
+
+/* string.h subset */
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+
+/* Implementations are from the Public Domain C Library (PDCLib). */
+static size_t strlen( const char * s )
+{
+    size_t rc = 0;
+    while ( s[rc] )
+    {
+        ++rc;
+    }
+    return rc;
+}
+
+static void * memcpy( void *s1, const void *s2, size_t n )
+{
+    char * dest = (char *) s1;
+    const char * src = (const char *) s2;
+    while ( n-- )
+    {
+        *dest++ = *src++;
+    }
+    return s1;
+}
+
+static void * memset( void * s, int c, size_t n )
+{
+    unsigned char * p = (unsigned char *) s;
+    while ( n-- )
+    {
+        *p++ = (unsigned char) c;
+    }
+    return s;
+}
+#endif
+
+#endif
diff --git a/third_party/nanopb/generator/nanopb_generator.py b/third_party/nanopb/generator/nanopb_generator.py
new file mode 100755
index 0000000..973c761
--- /dev/null
+++ b/third_party/nanopb/generator/nanopb_generator.py
@@ -0,0 +1,1602 @@
+#!/usr/bin/env python
+
+from __future__ import unicode_literals
+
+'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
+nanopb_version = "nanopb-0.3.7-dev"
+
+import sys
+import re
+from functools import reduce
+
+try:
+    # Add some dummy imports to keep packaging tools happy.
+    import google, distutils.util # bbfreeze seems to need these
+    import pkg_resources # pyinstaller / protobuf 2.5 seem to need these
+except:
+    # Don't care, we will error out later if it is actually important.
+    pass
+
+try:
+    import google.protobuf.text_format as text_format
+    import google.protobuf.descriptor_pb2 as descriptor
+except:
+    sys.stderr.write('''
+         *************************************************************
+         *** Could not import the Google protobuf Python libraries ***
+         *** Try installing package 'python-protobuf' or similar.  ***
+         *************************************************************
+    ''' + '\n')
+    raise
+
+try:
+    import proto.nanopb_pb2 as nanopb_pb2
+    import proto.plugin_pb2 as plugin_pb2
+except:
+    sys.stderr.write('''
+         ********************************************************************
+         *** Failed to import the protocol definitions for generator.     ***
+         *** You have to run 'make' in the nanopb/generator/proto folder. ***
+         ********************************************************************
+    ''' + '\n')
+    raise
+
+# ---------------------------------------------------------------------------
+#                     Generation of single fields
+# ---------------------------------------------------------------------------
+
+import time
+import os.path
+
+# Values are tuple (c type, pb type, encoded size, int_size_allowed)
+FieldD = descriptor.FieldDescriptorProto
+datatypes = {
+    FieldD.TYPE_BOOL:       ('bool',     'BOOL',        1,  False),
+    FieldD.TYPE_DOUBLE:     ('double',   'DOUBLE',      8,  False),
+    FieldD.TYPE_FIXED32:    ('uint32_t', 'FIXED32',     4,  False),
+    FieldD.TYPE_FIXED64:    ('uint64_t', 'FIXED64',     8,  False),
+    FieldD.TYPE_FLOAT:      ('float',    'FLOAT',       4,  False),
+    FieldD.TYPE_INT32:      ('int32_t',  'INT32',      10,  True),
+    FieldD.TYPE_INT64:      ('int64_t',  'INT64',      10,  True),
+    FieldD.TYPE_SFIXED32:   ('int32_t',  'SFIXED32',    4,  False),
+    FieldD.TYPE_SFIXED64:   ('int64_t',  'SFIXED64',    8,  False),
+    FieldD.TYPE_SINT32:     ('int32_t',  'SINT32',      5,  True),
+    FieldD.TYPE_SINT64:     ('int64_t',  'SINT64',     10,  True),
+    FieldD.TYPE_UINT32:     ('uint32_t', 'UINT32',      5,  True),
+    FieldD.TYPE_UINT64:     ('uint64_t', 'UINT64',     10,  True)
+}
+
+# Integer size overrides (from .proto settings)
+intsizes = {
+    nanopb_pb2.IS_8:     'int8_t',
+    nanopb_pb2.IS_16:    'int16_t',
+    nanopb_pb2.IS_32:    'int32_t',
+    nanopb_pb2.IS_64:    'int64_t',
+}
+
+# String types (for python 2 / python 3 compatibility)
+try:
+    strtypes = (unicode, str)
+except NameError:
+    strtypes = (str, )
+
+class Names:
+    '''Keeps a set of nested names and formats them to C identifier.'''
+    def __init__(self, parts = ()):
+        if isinstance(parts, Names):
+            parts = parts.parts
+        self.parts = tuple(parts)
+
+    def __str__(self):
+        return '_'.join(self.parts)
+
+    def __add__(self, other):
+        if isinstance(other, strtypes):
+            return Names(self.parts + (other,))
+        elif isinstance(other, tuple):
+            return Names(self.parts + other)
+        else:
+            raise ValueError("Name parts should be of type str")
+
+    def __eq__(self, other):
+        return isinstance(other, Names) and self.parts == other.parts
+
+def names_from_type_name(type_name):
+    '''Parse Names() from FieldDescriptorProto type_name'''
+    if type_name[0] != '.':
+        raise NotImplementedError("Lookup of non-absolute type names is not supported")
+    return Names(type_name[1:].split('.'))
+
+def varint_max_size(max_value):
+    '''Returns the maximum number of bytes a varint can take when encoded.'''
+    if max_value < 0:
+        max_value = 2**64 - max_value
+    for i in range(1, 11):
+        if (max_value >> (i * 7)) == 0:
+            return i
+    raise ValueError("Value too large for varint: " + str(max_value))
+
+assert varint_max_size(-1) == 10
+assert varint_max_size(0) == 1
+assert varint_max_size(127) == 1
+assert varint_max_size(128) == 2
+
+class EncodedSize:
+    '''Class used to represent the encoded size of a field or a message.
+    Consists of a combination of symbolic sizes and integer sizes.'''
+    def __init__(self, value = 0, symbols = []):
+        if isinstance(value, EncodedSize):
+            self.value = value.value
+            self.symbols = value.symbols
+        elif isinstance(value, strtypes + (Names,)):
+            self.symbols = [str(value)]
+            self.value = 0
+        else:
+            self.value = value
+            self.symbols = symbols
+
+    def __add__(self, other):
+        if isinstance(other, int):
+            return EncodedSize(self.value + other, self.symbols)
+        elif isinstance(other, strtypes + (Names,)):
+            return EncodedSize(self.value, self.symbols + [str(other)])
+        elif isinstance(other, EncodedSize):
+            return EncodedSize(self.value + other.value, self.symbols + other.symbols)
+        else:
+            raise ValueError("Cannot add size: " + repr(other))
+
+    def __mul__(self, other):
+        if isinstance(other, int):
+            return EncodedSize(self.value * other, [str(other) + '*' + s for s in self.symbols])
+        else:
+            raise ValueError("Cannot multiply size: " + repr(other))
+
+    def __str__(self):
+        if not self.symbols:
+            return str(self.value)
+        else:
+            return '(' + str(self.value) + ' + ' + ' + '.join(self.symbols) + ')'
+
+    def upperlimit(self):
+        if not self.symbols:
+            return self.value
+        else:
+            return 2**32 - 1
+
+class Enum:
+    def __init__(self, names, desc, enum_options):
+        '''desc is EnumDescriptorProto'''
+
+        self.options = enum_options
+        self.names = names + desc.name
+
+        if enum_options.long_names:
+            self.values = [(self.names + x.name, x.number) for x in desc.value]
+        else:
+            self.values = [(names + x.name, x.number) for x in desc.value]
+
+        self.value_longnames = [self.names + x.name for x in desc.value]
+        self.packed = enum_options.packed_enum
+
+    def has_negative(self):
+        for n, v in self.values:
+            if v < 0:
+                return True
+        return False
+
+    def encoded_size(self):
+        return max([varint_max_size(v) for n,v in self.values])
+
+    def __str__(self):
+        result = 'typedef enum _%s {\n' % self.names
+        result += ',\n'.join(["    %s = %d" % x for x in self.values])
+        result += '\n}'
+
+        if self.packed:
+            result += ' pb_packed'
+
+        result += ' %s;' % self.names
+
+        result += '\n#define _%s_MIN %s' % (self.names, self.values[0][0])
+        result += '\n#define _%s_MAX %s' % (self.names, self.values[-1][0])
+        result += '\n#define _%s_ARRAYSIZE ((%s)(%s+1))' % (self.names, self.names, self.values[-1][0])
+
+        if not self.options.long_names:
+            # Define the long names always so that enum value references
+            # from other files work properly.
+            for i, x in enumerate(self.values):
+                result += '\n#define %s %s' % (self.value_longnames[i], x[0])
+
+        return result
+
+class FieldMaxSize:
+    def __init__(self, worst = 0, checks = [], field_name = 'undefined'):
+        if isinstance(worst, list):
+            self.worst = max(i for i in worst if i is not None)
+        else:
+            self.worst = worst
+
+        self.worst_field = field_name
+        self.checks = list(checks)
+
+    def extend(self, extend, field_name = None):
+        self.worst = max(self.worst, extend.worst)
+
+        if self.worst == extend.worst:
+            self.worst_field = extend.worst_field
+
+        self.checks.extend(extend.checks)
+
+class Field:
+    def __init__(self, struct_name, desc, field_options):
+        '''desc is FieldDescriptorProto'''
+        self.tag = desc.number
+        self.struct_name = struct_name
+        self.union_name = None
+        self.name = desc.name
+        self.default = None
+        self.max_size = None
+        self.max_count = None
+        self.array_decl = ""
+        self.enc_size = None
+        self.ctype = None
+
+        self.inline = None
+        if field_options.type == nanopb_pb2.FT_INLINE:
+            field_options.type = nanopb_pb2.FT_STATIC
+            self.inline = nanopb_pb2.FT_INLINE
+
+        # Parse field options
+        if field_options.HasField("max_size"):
+            self.max_size = field_options.max_size
+
+        if field_options.HasField("max_count"):
+            self.max_count = field_options.max_count
+
+        if desc.HasField('default_value'):
+            self.default = desc.default_value
+
+        # Check field rules, i.e. required/optional/repeated.
+        can_be_static = True
+        if desc.label == FieldD.LABEL_REQUIRED:
+            self.rules = 'REQUIRED'
+        elif desc.label == FieldD.LABEL_OPTIONAL:
+            self.rules = 'OPTIONAL'
+        elif desc.label == FieldD.LABEL_REPEATED:
+            self.rules = 'REPEATED'
+            if self.max_count is None:
+                can_be_static = False
+            else:
+                self.array_decl = '[%d]' % self.max_count
+        else:
+            raise NotImplementedError(desc.label)
+
+        # Check if the field can be implemented with static allocation
+        # i.e. whether the data size is known.
+        if desc.type == FieldD.TYPE_STRING and self.max_size is None:
+            can_be_static = False
+
+        if desc.type == FieldD.TYPE_BYTES and self.max_size is None:
+            can_be_static = False
+
+        # Decide how the field data will be allocated
+        if field_options.type == nanopb_pb2.FT_DEFAULT:
+            if can_be_static:
+                field_options.type = nanopb_pb2.FT_STATIC
+            else:
+                field_options.type = nanopb_pb2.FT_CALLBACK
+
+        if field_options.type == nanopb_pb2.FT_STATIC and not can_be_static:
+            raise Exception("Field %s is defined as static, but max_size or "
+                            "max_count is not given." % self.name)
+
+        if field_options.type == nanopb_pb2.FT_STATIC:
+            self.allocation = 'STATIC'
+        elif field_options.type == nanopb_pb2.FT_POINTER:
+            self.allocation = 'POINTER'
+        elif field_options.type == nanopb_pb2.FT_CALLBACK:
+            self.allocation = 'CALLBACK'
+        else:
+            raise NotImplementedError(field_options.type)
+
+        # Decide the C data type to use in the struct.
+        if desc.type in datatypes:
+            self.ctype, self.pbtype, self.enc_size, isa = datatypes[desc.type]
+
+            # Override the field size if user wants to use smaller integers
+            if isa and field_options.int_size != nanopb_pb2.IS_DEFAULT:
+                self.ctype = intsizes[field_options.int_size]
+                if desc.type == FieldD.TYPE_UINT32 or desc.type == FieldD.TYPE_UINT64:
+                    self.ctype = 'u' + self.ctype;
+        elif desc.type == FieldD.TYPE_ENUM:
+            self.pbtype = 'ENUM'
+            self.ctype = names_from_type_name(desc.type_name)
+            if self.default is not None:
+                self.default = self.ctype + self.default
+            self.enc_size = None # Needs to be filled in when enum values are known
+        elif desc.type == FieldD.TYPE_STRING:
+            self.pbtype = 'STRING'
+            self.ctype = 'char'
+            if self.allocation == 'STATIC':
+                self.ctype = 'char'
+                self.array_decl += '[%d]' % self.max_size
+                self.enc_size = varint_max_size(self.max_size) + self.max_size
+        elif desc.type == FieldD.TYPE_BYTES:
+            self.pbtype = 'BYTES'
+            if self.allocation == 'STATIC':
+                # Inline STATIC for BYTES is like STATIC for STRING.
+                if self.inline:
+                    self.ctype = 'pb_byte_t'
+                    self.array_decl += '[%d]' % self.max_size
+                else:
+                    self.ctype = self.struct_name + self.name + 't'
+                self.enc_size = varint_max_size(self.max_size) + self.max_size
+            elif self.allocation == 'POINTER':
+                self.ctype = 'pb_bytes_array_t'
+        elif desc.type == FieldD.TYPE_MESSAGE:
+            self.pbtype = 'MESSAGE'
+            self.ctype = self.submsgname = names_from_type_name(desc.type_name)
+            self.enc_size = None # Needs to be filled in after the message type is available
+        else:
+            raise NotImplementedError(desc.type)
+
+    def __lt__(self, other):
+        return self.tag < other.tag
+
+    def __str__(self):
+        result = ''
+        if self.allocation == 'POINTER':
+            if self.rules == 'REPEATED':
+                result += '    pb_size_t ' + self.name + '_count;\n'
+
+            if self.pbtype == 'MESSAGE':
+                # Use struct definition, so recursive submessages are possible
+                result += '    struct _%s *%s;' % (self.ctype, self.name)
+            elif self.rules == 'REPEATED' and self.pbtype in ['STRING', 'BYTES']:
+                # String/bytes arrays need to be defined as pointers to pointers
+                result += '    %s **%s;' % (self.ctype, self.name)
+            else:
+                result += '    %s *%s;' % (self.ctype, self.name)
+        elif self.allocation == 'CALLBACK':
+            result += '    pb_callback_t %s;' % self.name
+        else:
+            if self.rules == 'OPTIONAL' and self.allocation == 'STATIC':
+                result += '    bool has_' + self.name + ';\n'
+            elif self.rules == 'REPEATED' and self.allocation == 'STATIC':
+                result += '    pb_size_t ' + self.name + '_count;\n'
+            result += '    %s %s%s;' % (self.ctype, self.name, self.array_decl)
+        return result
+
+    def types(self):
+        '''Return definitions for any special types this field might need.'''
+        if self.pbtype == 'BYTES' and self.allocation == 'STATIC' and not self.inline:
+            result = 'typedef PB_BYTES_ARRAY_T(%d) %s;\n' % (self.max_size, self.ctype)
+        else:
+            result = ''
+        return result
+
+    def get_dependencies(self):
+        '''Get list of type names used by this field.'''
+        if self.allocation == 'STATIC':
+            return [str(self.ctype)]
+        else:
+            return []
+
+    def get_initializer(self, null_init, inner_init_only = False):
+        '''Return literal expression for this field's default value.
+        null_init: If True, initialize to a 0 value instead of default from .proto
+        inner_init_only: If True, exclude initialization for any count/has fields
+        '''
+
+        inner_init = None
+        if self.pbtype == 'MESSAGE':
+            if null_init:
+                inner_init = '%s_init_zero' % self.ctype
+            else:
+                inner_init = '%s_init_default' % self.ctype
+        elif self.default is None or null_init:
+            if self.pbtype == 'STRING':
+                inner_init = '""'
+            elif self.pbtype == 'BYTES':
+                if self.inline:
+                    inner_init = '{0}'
+                else:
+                    inner_init = '{0, {0}}'
+            elif self.pbtype in ('ENUM', 'UENUM'):
+                inner_init = '(%s)0' % self.ctype
+            else:
+                inner_init = '0'
+        else:
+            if self.pbtype == 'STRING':
+                inner_init = self.default.replace('"', '\\"')
+                inner_init = '"' + inner_init + '"'
+            elif self.pbtype == 'BYTES':
+                data = ['0x%02x' % ord(c) for c in self.default]
+                if len(data) == 0:
+                    if self.inline:
+                        inner_init = '{0}'
+                    else:
+                        inner_init = '{0, {0}}'
+                else:
+                    if self.inline:
+                        inner_init = '{%s}' % ','.join(data)
+                    else:
+                        inner_init = '{%d, {%s}}' % (len(data), ','.join(data))
+            elif self.pbtype in ['FIXED32', 'UINT32']:
+                inner_init = str(self.default) + 'u'
+            elif self.pbtype in ['FIXED64', 'UINT64']:
+                inner_init = str(self.default) + 'ull'
+            elif self.pbtype in ['SFIXED64', 'INT64']:
+                inner_init = str(self.default) + 'll'
+            else:
+                inner_init = str(self.default)
+
+        if inner_init_only:
+            return inner_init
+
+        outer_init = None
+        if self.allocation == 'STATIC':
+            if self.rules == 'REPEATED':
+                outer_init = '0, {'
+                outer_init += ', '.join([inner_init] * self.max_count)
+                outer_init += '}'
+            elif self.rules == 'OPTIONAL':
+                outer_init = 'false, ' + inner_init
+            else:
+                outer_init = inner_init
+        elif self.allocation == 'POINTER':
+            if self.rules == 'REPEATED':
+                outer_init = '0, NULL'
+            else:
+                outer_init = 'NULL'
+        elif self.allocation == 'CALLBACK':
+            if self.pbtype == 'EXTENSION':
+                outer_init = 'NULL'
+            else:
+                outer_init = '{{NULL}, NULL}'
+
+        return outer_init
+
+    def default_decl(self, declaration_only = False):
+        '''Return definition for this field's default value.'''
+        if self.default is None:
+            return None
+
+        ctype = self.ctype
+        default = self.get_initializer(False, True)
+        array_decl = ''
+
+        if self.pbtype == 'STRING':
+            if self.allocation != 'STATIC':
+                return None # Not implemented
+            array_decl = '[%d]' % self.max_size
+        elif self.pbtype == 'BYTES':
+            if self.allocation != 'STATIC':
+                return None # Not implemented
+            if self.inline:
+                array_decl = '[%d]' % self.max_size
+
+        if declaration_only:
+            return 'extern const %s %s_default%s;' % (ctype, self.struct_name + self.name, array_decl)
+        else:
+            return 'const %s %s_default%s = %s;' % (ctype, self.struct_name + self.name, array_decl, default)
+
+    def tags(self):
+        '''Return the #define for the tag number of this field.'''
+        identifier = '%s_%s_tag' % (self.struct_name, self.name)
+        return '#define %-40s %d\n' % (identifier, self.tag)
+
+    def pb_field_t(self, prev_field_name):
+        '''Return the pb_field_t initializer to use in the constant array.
+        prev_field_name is the name of the previous field or None.
+        '''
+
+        if self.rules == 'ONEOF':
+            if self.anonymous:
+                result = '    PB_ANONYMOUS_ONEOF_FIELD(%s, ' % self.union_name
+            else:
+                result = '    PB_ONEOF_FIELD(%s, ' % self.union_name
+        else:
+            result = '    PB_FIELD('
+
+        result += '%3d, ' % self.tag
+        result += '%-8s, ' % self.pbtype
+        result += '%s, ' % self.rules
+        result += '%-8s, ' % (self.allocation if not self.inline else "INLINE")
+        result += '%s, ' % ("FIRST" if not prev_field_name else "OTHER")
+        result += '%s, ' % self.struct_name
+        result += '%s, ' % self.name
+        result += '%s, ' % (prev_field_name or self.name)
+
+        if self.pbtype == 'MESSAGE':
+            result += '&%s_fields)' % self.submsgname
+        elif self.default is None:
+            result += '0)'
+        elif self.pbtype in ['BYTES', 'STRING'] and self.allocation != 'STATIC':
+            result += '0)' # Arbitrary size default values not implemented
+        elif self.rules == 'OPTEXT':
+            result += '0)' # Default value for extensions is not implemented
+        else:
+            result += '&%s_default)' % (self.struct_name + self.name)
+
+        return result
+
+    def get_last_field_name(self):
+        return self.name
+
+    def largest_field_value(self):
+        '''Determine if this field needs 16bit or 32bit pb_field_t structure to compile properly.
+        Returns numeric value or a C-expression for assert.'''
+        check = []
+        if self.pbtype == 'MESSAGE':
+            if self.rules == 'REPEATED' and self.allocation == 'STATIC':
+                check.append('pb_membersize(%s, %s[0])' % (self.struct_name, self.name))
+            elif self.rules == 'ONEOF':
+                if self.anonymous:
+                    check.append('pb_membersize(%s, %s)' % (self.struct_name, self.name))
+                else:
+                    check.append('pb_membersize(%s, %s.%s)' % (self.struct_name, self.union_name, self.name))
+            else:
+                check.append('pb_membersize(%s, %s)' % (self.struct_name, self.name))
+
+        return FieldMaxSize([self.tag, self.max_size, self.max_count],
+                            check,
+                            ('%s.%s' % (self.struct_name, self.name)))
+
+    def encoded_size(self, dependencies):
+        '''Return the maximum size that this field can take when encoded,
+        including the field tag. If the size cannot be determined, returns
+        None.'''
+
+        if self.allocation != 'STATIC':
+            return None
+
+        if self.pbtype == 'MESSAGE':
+            encsize = None
+            if str(self.submsgname) in dependencies:
+                submsg = dependencies[str(self.submsgname)]
+                encsize = submsg.encoded_size(dependencies)
+                if encsize is not None:
+                    # Include submessage length prefix
+                    encsize += varint_max_size(encsize.upperlimit())
+
+            if encsize is None:
+                # Submessage or its size cannot be found.
+                # This can occur if submessage is defined in different
+                # file, and it or its .options could not be found.
+                # Instead of direct numeric value, reference the size that
+                # has been #defined in the other file.
+                encsize = EncodedSize(self.submsgname + 'size')
+
+                # We will have to make a conservative assumption on the length
+                # prefix size, though.
+                encsize += 5
+
+        elif self.pbtype in ['ENUM', 'UENUM']:
+            if str(self.ctype) in dependencies:
+                enumtype = dependencies[str(self.ctype)]
+                encsize = enumtype.encoded_size()
+            else:
+                # Conservative assumption
+                encsize = 10
+
+        elif self.enc_size is None:
+            raise RuntimeError("Could not determine encoded size for %s.%s"
+                               % (self.struct_name, self.name))
+        else:
+            encsize = EncodedSize(self.enc_size)
+
+        encsize += varint_max_size(self.tag << 3) # Tag + wire type
+
+        if self.rules == 'REPEATED':
+            # Decoders must be always able to handle unpacked arrays.
+            # Therefore we have to reserve space for it, even though
+            # we emit packed arrays ourselves.
+            encsize *= self.max_count
+
+        return encsize
+
+
+class ExtensionRange(Field):
+    def __init__(self, struct_name, range_start, field_options):
+        '''Implements a special pb_extension_t* field in an extensible message
+        structure. The range_start signifies the index at which the extensions
+        start. Not necessarily all tags above this are extensions, it is merely
+        a speed optimization.
+        '''
+        self.tag = range_start
+        self.struct_name = struct_name
+        self.name = 'extensions'
+        self.pbtype = 'EXTENSION'
+        self.rules = 'OPTIONAL'
+        self.allocation = 'CALLBACK'
+        self.ctype = 'pb_extension_t'
+        self.array_decl = ''
+        self.default = None
+        self.max_size = 0
+        self.max_count = 0
+        self.inline = None
+
+    def __str__(self):
+        return '    pb_extension_t *extensions;'
+
+    def types(self):
+        return ''
+
+    def tags(self):
+        return ''
+
+    def encoded_size(self, dependencies):
+        # We exclude extensions from the count, because they cannot be known
+        # until runtime. Other option would be to return None here, but this
+        # way the value remains useful if extensions are not used.
+        return EncodedSize(0)
+
+class ExtensionField(Field):
+    def __init__(self, struct_name, desc, field_options):
+        self.fullname = struct_name + desc.name
+        self.extendee_name = names_from_type_name(desc.extendee)
+        Field.__init__(self, self.fullname + 'struct', desc, field_options)
+
+        if self.rules != 'OPTIONAL':
+            self.skip = True
+        else:
+            self.skip = False
+            self.rules = 'OPTEXT'
+
+    def tags(self):
+        '''Return the #define for the tag number of this field.'''
+        identifier = '%s_tag' % self.fullname
+        return '#define %-40s %d\n' % (identifier, self.tag)
+
+    def extension_decl(self):
+        '''Declaration of the extension type in the .pb.h file'''
+        if self.skip:
+            msg = '/* Extension field %s was skipped because only "optional"\n' % self.fullname
+            msg +='   type of extension fields is currently supported. */\n'
+            return msg
+
+        return ('extern const pb_extension_type_t %s; /* field type: %s */\n' %
+            (self.fullname, str(self).strip()))
+
+    def extension_def(self):
+        '''Definition of the extension type in the .pb.c file'''
+
+        if self.skip:
+            return ''
+
+        result  = 'typedef struct {\n'
+        result += str(self)
+        result += '\n} %s;\n\n' % self.struct_name
+        result += ('static const pb_field_t %s_field = \n  %s;\n\n' %
+                    (self.fullname, self.pb_field_t(None)))
+        result += 'const pb_extension_type_t %s = {\n' % self.fullname
+        result += '    NULL,\n'
+        result += '    NULL,\n'
+        result += '    &%s_field\n' % self.fullname
+        result += '};\n'
+        return result
+
+
+# ---------------------------------------------------------------------------
+#                   Generation of oneofs (unions)
+# ---------------------------------------------------------------------------
+
+class OneOf(Field):
+    def __init__(self, struct_name, oneof_desc):
+        self.struct_name = struct_name
+        self.name = oneof_desc.name
+        self.ctype = 'union'
+        self.pbtype = 'oneof'
+        self.fields = []
+        self.allocation = 'ONEOF'
+        self.default = None
+        self.rules = 'ONEOF'
+        self.anonymous = False
+        self.inline = None
+
+    def add_field(self, field):
+        if field.allocation == 'CALLBACK':
+            raise Exception("Callback fields inside of oneof are not supported"
+                            + " (field %s)" % field.name)
+
+        field.union_name = self.name
+        field.rules = 'ONEOF'
+        field.anonymous = self.anonymous
+        self.fields.append(field)
+        self.fields.sort(key = lambda f: f.tag)
+
+        # Sort by the lowest tag number inside union
+        self.tag = min([f.tag for f in self.fields])
+
+    def __str__(self):
+        result = ''
+        if self.fields:
+            result += '    pb_size_t which_' + self.name + ";\n"
+            result += '    union {\n'
+            for f in self.fields:
+                result += '    ' + str(f).replace('\n', '\n    ') + '\n'
+            if self.anonymous:
+                result += '    };'
+            else:
+                result += '    } ' + self.name + ';'
+        return result
+
+    def types(self):
+        return ''.join([f.types() for f in self.fields])
+
+    def get_dependencies(self):
+        deps = []
+        for f in self.fields:
+            deps += f.get_dependencies()
+        return deps
+
+    def get_initializer(self, null_init):
+        return '0, {' + self.fields[0].get_initializer(null_init) + '}'
+
+    def default_decl(self, declaration_only = False):
+        return None
+
+    def tags(self):
+        return ''.join([f.tags() for f in self.fields])
+
+    def pb_field_t(self, prev_field_name):
+        result = ',\n'.join([f.pb_field_t(prev_field_name) for f in self.fields])
+        return result
+
+    def get_last_field_name(self):
+        if self.anonymous:
+            return self.fields[-1].name
+        else:
+            return self.name + '.' + self.fields[-1].name
+
+    def largest_field_value(self):
+        largest = FieldMaxSize()
+        for f in self.fields:
+            largest.extend(f.largest_field_value())
+        return largest
+
+    def encoded_size(self, dependencies):
+        '''Returns the size of the largest oneof field.'''
+        largest = EncodedSize(0)
+        for f in self.fields:
+            size = EncodedSize(f.encoded_size(dependencies))
+            if size.value is None:
+                return None
+            elif size.symbols:
+                return None # Cannot resolve maximum of symbols
+            elif size.value > largest.value:
+                largest = size
+
+        return largest
+
+# ---------------------------------------------------------------------------
+#                   Generation of messages (structures)
+# ---------------------------------------------------------------------------
+
+
+class Message:
+    def __init__(self, names, desc, message_options):
+        self.name = names
+        self.fields = []
+        self.oneofs = {}
+        no_unions = []
+
+        if message_options.msgid:
+            self.msgid = message_options.msgid
+
+        if hasattr(desc, 'oneof_decl'):
+            for i, f in enumerate(desc.oneof_decl):
+                oneof_options = get_nanopb_suboptions(desc, message_options, self.name + f.name)
+                if oneof_options.no_unions:
+                    no_unions.append(i) # No union, but add fields normally
+                elif oneof_options.type == nanopb_pb2.FT_IGNORE:
+                    pass # No union and skip fields also
+                else:
+                    oneof = OneOf(self.name, f)
+                    if oneof_options.anonymous_oneof:
+                        oneof.anonymous = True
+                    self.oneofs[i] = oneof
+                    self.fields.append(oneof)
+
+        for f in desc.field:
+            field_options = get_nanopb_suboptions(f, message_options, self.name + f.name)
+            if field_options.type == nanopb_pb2.FT_IGNORE:
+                continue
+
+            field = Field(self.name, f, field_options)
+            if (hasattr(f, 'oneof_index') and
+                f.HasField('oneof_index') and
+                f.oneof_index not in no_unions):
+                if f.oneof_index in self.oneofs:
+                    self.oneofs[f.oneof_index].add_field(field)
+            else:
+                self.fields.append(field)
+
+        if len(desc.extension_range) > 0:
+            field_options = get_nanopb_suboptions(desc, message_options, self.name + 'extensions')
+            range_start = min([r.start for r in desc.extension_range])
+            if field_options.type != nanopb_pb2.FT_IGNORE:
+                self.fields.append(ExtensionRange(self.name, range_start, field_options))
+
+        self.packed = message_options.packed_struct
+        self.ordered_fields = self.fields[:]
+        self.ordered_fields.sort()
+
+    def get_dependencies(self):
+        '''Get list of type names that this structure refers to.'''
+        deps = []
+        for f in self.fields:
+            deps += f.get_dependencies()
+        return deps
+
+    def __str__(self):
+        result = 'typedef struct _%s {\n' % self.name
+
+        if not self.ordered_fields:
+            # Empty structs are not allowed in C standard.
+            # Therefore add a dummy field if an empty message occurs.
+            result += '    char dummy_field;'
+
+        result += '\n'.join([str(f) for f in self.ordered_fields])
+        result += '\n/* @@protoc_insertion_point(struct:%s) */' % self.name
+        result += '\n}'
+
+        if self.packed:
+            result += ' pb_packed'
+
+        result += ' %s;' % self.name
+
+        if self.packed:
+            result = 'PB_PACKED_STRUCT_START\n' + result
+            result += '\nPB_PACKED_STRUCT_END'
+
+        return result
+
+    def types(self):
+        return ''.join([f.types() for f in self.fields])
+
+    def get_initializer(self, null_init):
+        if not self.ordered_fields:
+            return '{0}'
+
+        parts = []
+        for field in self.ordered_fields:
+            parts.append(field.get_initializer(null_init))
+        return '{' + ', '.join(parts) + '}'
+
+    def default_decl(self, declaration_only = False):
+        result = ""
+        for field in self.fields:
+            default = field.default_decl(declaration_only)
+            if default is not None:
+                result += default + '\n'
+        return result
+
+    def count_required_fields(self):
+        '''Returns number of required fields inside this message'''
+        count = 0
+        for f in self.fields:
+            if not isinstance(f, OneOf):
+                if f.rules == 'REQUIRED':
+                    count += 1
+        return count
+
+    def count_all_fields(self):
+        count = 0
+        for f in self.fields:
+            if isinstance(f, OneOf):
+                count += len(f.fields)
+            else:
+                count += 1
+        return count
+
+    def fields_declaration(self):
+        result = 'extern const pb_field_t %s_fields[%d];' % (self.name, self.count_all_fields() + 1)
+        return result
+
+    def fields_definition(self):
+        result = 'const pb_field_t %s_fields[%d] = {\n' % (self.name, self.count_all_fields() + 1)
+
+        prev = None
+        for field in self.ordered_fields:
+            result += field.pb_field_t(prev)
+            result += ',\n'
+            prev = field.get_last_field_name()
+
+        result += '    PB_LAST_FIELD\n};'
+        return result
+
+    def encoded_size(self, dependencies):
+        '''Return the maximum size that this message can take when encoded.
+        If the size cannot be determined, returns None.
+        '''
+        size = EncodedSize(0)
+        for field in self.fields:
+            fsize = field.encoded_size(dependencies)
+            if fsize is None:
+                return None
+            size += fsize
+
+        return size
+
+
+# ---------------------------------------------------------------------------
+#                    Processing of entire .proto files
+# ---------------------------------------------------------------------------
+
+def iterate_messages(desc, names = Names()):
+    '''Recursively find all messages. For each, yield name, DescriptorProto.'''
+    if hasattr(desc, 'message_type'):
+        submsgs = desc.message_type
+    else:
+        submsgs = desc.nested_type
+
+    for submsg in submsgs:
+        sub_names = names + submsg.name
+        yield sub_names, submsg
+
+        for x in iterate_messages(submsg, sub_names):
+            yield x
+
+def iterate_extensions(desc, names = Names()):
+    '''Recursively find all extensions.
+    For each, yield name, FieldDescriptorProto.
+    '''
+    for extension in desc.extension:
+        yield names, extension
+
+    for subname, subdesc in iterate_messages(desc, names):
+        for extension in subdesc.extension:
+            yield subname, extension
+
+def toposort2(data):
+    '''Topological sort.
+    From http://code.activestate.com/recipes/577413-topological-sort/
+    This function is under the MIT license.
+    '''
+    for k, v in list(data.items()):
+        v.discard(k) # Ignore self dependencies
+    extra_items_in_deps = reduce(set.union, list(data.values()), set()) - set(data.keys())
+    data.update(dict([(item, set()) for item in extra_items_in_deps]))
+    while True:
+        ordered = set(item for item,dep in list(data.items()) if not dep)
+        if not ordered:
+            break
+        for item in sorted(ordered):
+            yield item
+        data = dict([(item, (dep - ordered)) for item,dep in list(data.items())
+                if item not in ordered])
+    assert not data, "A cyclic dependency exists amongst %r" % data
+
+def sort_dependencies(messages):
+    '''Sort a list of Messages based on dependencies.'''
+    dependencies = {}
+    message_by_name = {}
+    for message in messages:
+        dependencies[str(message.name)] = set(message.get_dependencies())
+        message_by_name[str(message.name)] = message
+
+    for msgname in toposort2(dependencies):
+        if msgname in message_by_name:
+            yield message_by_name[msgname]
+
+def make_identifier(headername):
+    '''Make #ifndef identifier that contains uppercase A-Z and digits 0-9'''
+    result = ""
+    for c in headername.upper():
+        if c.isalnum():
+            result += c
+        else:
+            result += '_'
+    return result
+
+class ProtoFile:
+    def __init__(self, fdesc, file_options):
+        '''Takes a FileDescriptorProto and parses it.'''
+        self.fdesc = fdesc
+        self.file_options = file_options
+        self.dependencies = {}
+        self.parse()
+
+        # Some of types used in this file probably come from the file itself.
+        # Thus it has implicit dependency on itself.
+        self.add_dependency(self)
+
+    def parse(self):
+        self.enums = []
+        self.messages = []
+        self.extensions = []
+
+        if self.fdesc.package:
+            base_name = Names(self.fdesc.package.split('.'))
+        else:
+            base_name = Names()
+
+        for enum in self.fdesc.enum_type:
+            enum_options = get_nanopb_suboptions(enum, self.file_options, base_name + enum.name)
+            self.enums.append(Enum(base_name, enum, enum_options))
+
+        for names, message in iterate_messages(self.fdesc, base_name):
+            message_options = get_nanopb_suboptions(message, self.file_options, names)
+
+            if message_options.skip_message:
+                continue
+
+            self.messages.append(Message(names, message, message_options))
+            for enum in message.enum_type:
+                enum_options = get_nanopb_suboptions(enum, message_options, names + enum.name)
+                self.enums.append(Enum(names, enum, enum_options))
+
+        for names, extension in iterate_extensions(self.fdesc, base_name):
+            field_options = get_nanopb_suboptions(extension, self.file_options, names + extension.name)
+            if field_options.type != nanopb_pb2.FT_IGNORE:
+                self.extensions.append(ExtensionField(names, extension, field_options))
+
+    def add_dependency(self, other):
+        for enum in other.enums:
+            self.dependencies[str(enum.names)] = enum
+
+        for msg in other.messages:
+            self.dependencies[str(msg.name)] = msg
+
+        # Fix field default values where enum short names are used.
+        for enum in other.enums:
+            if not enum.options.long_names:
+                for message in self.messages:
+                    for field in message.fields:
+                        if field.default in enum.value_longnames:
+                            idx = enum.value_longnames.index(field.default)
+                            field.default = enum.values[idx][0]
+
+        # Fix field data types where enums have negative values.
+        for enum in other.enums:
+            if not enum.has_negative():
+                for message in self.messages:
+                    for field in message.fields:
+                        if field.pbtype == 'ENUM' and field.ctype == enum.names:
+                            field.pbtype = 'UENUM'
+
+    def generate_header(self, includes, headername, options):
+        '''Generate content for a header file.
+        Generates strings, which should be concatenated and stored to file.
+        '''
+
+        yield '/* Automatically generated nanopb header */\n'
+        if options.notimestamp:
+            yield '/* Generated by %s */\n\n' % (nanopb_version)
+        else:
+            yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
+
+        if self.fdesc.package:
+            symbol = make_identifier(self.fdesc.package + '_' + headername)
+        else:
+            symbol = make_identifier(headername)
+        yield '#ifndef PB_%s_INCLUDED\n' % symbol
+        yield '#define PB_%s_INCLUDED\n' % symbol
+        try:
+            yield options.libformat % ('pb.h')
+        except TypeError:
+            # no %s specified - use whatever was passed in as options.libformat
+            yield options.libformat
+        yield '\n'
+
+        for incfile in includes:
+            noext = os.path.splitext(incfile)[0]
+            yield options.genformat % (noext + options.extension + '.h')
+            yield '\n'
+
+        yield '/* @@protoc_insertion_point(includes) */\n'
+
+        yield '#if PB_PROTO_HEADER_VERSION != 30\n'
+        yield '#error Regenerate this file with the current version of nanopb generator.\n'
+        yield '#endif\n'
+        yield '\n'
+
+        yield '#ifdef __cplusplus\n'
+        yield 'extern "C" {\n'
+        yield '#endif\n\n'
+
+        if self.enums:
+            yield '/* Enum definitions */\n'
+            for enum in self.enums:
+                yield str(enum) + '\n\n'
+
+        if self.messages:
+            yield '/* Struct definitions */\n'
+            for msg in sort_dependencies(self.messages):
+                yield msg.types()
+                yield str(msg) + '\n\n'
+
+        if self.extensions:
+            yield '/* Extensions */\n'
+            for extension in self.extensions:
+                yield extension.extension_decl()
+            yield '\n'
+
+        if self.messages:
+            yield '/* Default values for struct fields */\n'
+            for msg in self.messages:
+                yield msg.default_decl(True)
+            yield '\n'
+
+            yield '/* Initializer values for message structs */\n'
+            for msg in self.messages:
+                identifier = '%s_init_default' % msg.name
+                yield '#define %-40s %s\n' % (identifier, msg.get_initializer(False))
+            for msg in self.messages:
+                identifier = '%s_init_zero' % msg.name
+                yield '#define %-40s %s\n' % (identifier, msg.get_initializer(True))
+            yield '\n'
+
+            yield '/* Field tags (for use in manual encoding/decoding) */\n'
+            for msg in sort_dependencies(self.messages):
+                for field in msg.fields:
+                    yield field.tags()
+            for extension in self.extensions:
+                yield extension.tags()
+            yield '\n'
+
+            yield '/* Struct field encoding specification for nanopb */\n'
+            for msg in self.messages:
+                yield msg.fields_declaration() + '\n'
+            yield '\n'
+
+            yield '/* Maximum encoded size of messages (where known) */\n'
+            for msg in self.messages:
+                msize = msg.encoded_size(self.dependencies)
+                identifier = '%s_size' % msg.name
+                if msize is not None:
+                    yield '#define %-40s %s\n' % (identifier, msize)
+                else:
+                    yield '/* %s depends on runtime parameters */\n' % identifier
+            yield '\n'
+
+            yield '/* Message IDs (where set with "msgid" option) */\n'
+
+            yield '#ifdef PB_MSGID\n'
+            for msg in self.messages:
+                if hasattr(msg,'msgid'):
+                    yield '#define PB_MSG_%d %s\n' % (msg.msgid, msg.name)
+            yield '\n'
+
+            symbol = make_identifier(headername.split('.')[0])
+            yield '#define %s_MESSAGES \\\n' % symbol
+
+            for msg in self.messages:
+                m = "-1"
+                msize = msg.encoded_size(self.dependencies)
+                if msize is not None:
+                    m = msize
+                if hasattr(msg,'msgid'):
+                    yield '\tPB_MSG(%d,%s,%s) \\\n' % (msg.msgid, m, msg.name)
+            yield '\n'
+
+            for msg in self.messages:
+                if hasattr(msg,'msgid'):
+                    yield '#define %s_msgid %d\n' % (msg.name, msg.msgid)
+            yield '\n'
+
+            yield '#endif\n\n'
+
+        yield '#ifdef __cplusplus\n'
+        yield '} /* extern "C" */\n'
+        yield '#endif\n'
+
+        # End of header
+        yield '/* @@protoc_insertion_point(eof) */\n'
+        yield '\n#endif\n'
+
+    def generate_source(self, headername, options):
+        '''Generate content for a source file.'''
+
+        yield '/* Automatically generated nanopb constant definitions */\n'
+        if options.notimestamp:
+            yield '/* Generated by %s */\n\n' % (nanopb_version)
+        else:
+            yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
+        yield options.genformat % (headername)
+        yield '\n'
+        yield '/* @@protoc_insertion_point(includes) */\n'
+
+        yield '#if PB_PROTO_HEADER_VERSION != 30\n'
+        yield '#error Regenerate this file with the current version of nanopb generator.\n'
+        yield '#endif\n'
+        yield '\n'
+
+        for msg in self.messages:
+            yield msg.default_decl(False)
+
+        yield '\n\n'
+
+        for msg in self.messages:
+            yield msg.fields_definition() + '\n\n'
+
+        for ext in self.extensions:
+            yield ext.extension_def() + '\n'
+
+        # Add checks for numeric limits
+        if self.messages:
+            largest_msg = max(self.messages, key = lambda m: m.count_required_fields())
+            largest_count = largest_msg.count_required_fields()
+            if largest_count > 64:
+                yield '\n/* Check that missing required fields will be properly detected */\n'
+                yield '#if PB_MAX_REQUIRED_FIELDS < %d\n' % largest_count
+                yield '#error Properly detecting missing required fields in %s requires \\\n' % largest_msg.name
+                yield '       setting PB_MAX_REQUIRED_FIELDS to %d or more.\n' % largest_count
+                yield '#endif\n'
+
+        max_field = FieldMaxSize()
+        checks_msgnames = []
+        for msg in self.messages:
+            checks_msgnames.append(msg.name)
+            for field in msg.fields:
+                max_field.extend(field.largest_field_value())
+
+        worst = max_field.worst
+        worst_field = max_field.worst_field
+        checks = max_field.checks
+
+        if worst > 255 or checks:
+            yield '\n/* Check that field information fits in pb_field_t */\n'
+
+            if worst > 65535 or checks:
+                yield '#if !defined(PB_FIELD_32BIT)\n'
+                if worst > 65535:
+                    yield '#error Field descriptor for %s is too large. Define PB_FIELD_32BIT to fix this.\n' % worst_field
+                else:
+                    assertion = ' && '.join(str(c) + ' < 65536' for c in checks)
+                    msgs = '_'.join(str(n) for n in checks_msgnames)
+                    yield '/* If you get an error here, it means that you need to define PB_FIELD_32BIT\n'
+                    yield ' * compile-time option. You can do that in pb.h or on compiler command line.\n'
+                    yield ' * \n'
+                    yield ' * The reason you need to do this is that some of your messages contain tag\n'
+                    yield ' * numbers or field sizes that are larger than what can fit in 8 or 16 bit\n'
+                    yield ' * field descriptors.\n'
+                    yield ' */\n'
+                    yield 'PB_STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_%s)\n'%(assertion,msgs)
+                yield '#endif\n\n'
+
+            if worst < 65536:
+                yield '#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)\n'
+                if worst > 255:
+                    yield '#error Field descriptor for %s is too large. Define PB_FIELD_16BIT to fix this.\n' % worst_field
+                else:
+                    assertion = ' && '.join(str(c) + ' < 256' for c in checks)
+                    msgs = '_'.join(str(n) for n in checks_msgnames)
+                    yield '/* If you get an error here, it means that you need to define PB_FIELD_16BIT\n'
+                    yield ' * compile-time option. You can do that in pb.h or on compiler command line.\n'
+                    yield ' * \n'
+                    yield ' * The reason you need to do this is that some of your messages contain tag\n'
+                    yield ' * numbers or field sizes that are larger than what can fit in the default\n'
+                    yield ' * 8 bit descriptors.\n'
+                    yield ' */\n'
+                    yield 'PB_STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_%s)\n'%(assertion,msgs)
+                yield '#endif\n\n'
+
+        # Add check for sizeof(double)
+        has_double = False
+        for msg in self.messages:
+            for field in msg.fields:
+                if field.ctype == 'double':
+                    has_double = True
+
+        if has_double:
+            yield '\n'
+            yield '/* On some platforms (such as AVR), double is really float.\n'
+            yield ' * These are not directly supported by nanopb, but see example_avr_double.\n'
+            yield ' * To get rid of this error, remove any double fields from your .proto.\n'
+            yield ' */\n'
+            yield 'PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)\n'
+
+        yield '\n'
+        yield '/* @@protoc_insertion_point(eof) */\n'
+
+# ---------------------------------------------------------------------------
+#                    Options parsing for the .proto files
+# ---------------------------------------------------------------------------
+
+from fnmatch import fnmatch
+
+def read_options_file(infile):
+    '''Parse a separate options file to list:
+        [(namemask, options), ...]
+    '''
+    results = []
+    data = infile.read()
+    data = re.sub('/\*.*?\*/', '', data, flags = re.MULTILINE)
+    data = re.sub('//.*?$', '', data, flags = re.MULTILINE)
+    data = re.sub('#.*?$', '', data, flags = re.MULTILINE)
+    for i, line in enumerate(data.split('\n')):
+        line = line.strip()
+        if not line:
+            continue
+
+        parts = line.split(None, 1)
+
+        if len(parts) < 2:
+            sys.stderr.write("%s:%d: " % (infile.name, i + 1) +
+                             "Option lines should have space between field name and options. " +
+                             "Skipping line: '%s'\n" % line)
+            continue
+
+        opts = nanopb_pb2.NanoPBOptions()
+
+        try:
+            text_format.Merge(parts[1], opts)
+        except Exception as e:
+            sys.stderr.write("%s:%d: " % (infile.name, i + 1) +
+                             "Unparseable option line: '%s'. " % line +
+                             "Error: %s\n" % str(e))
+            continue
+        results.append((parts[0], opts))
+
+    return results
+
+class Globals:
+    '''Ugly global variables, should find a good way to pass these.'''
+    verbose_options = False
+    separate_options = []
+    matched_namemasks = set()
+
+def get_nanopb_suboptions(subdesc, options, name):
+    '''Get copy of options, and merge information from subdesc.'''
+    new_options = nanopb_pb2.NanoPBOptions()
+    new_options.CopyFrom(options)
+
+    # Handle options defined in a separate file
+    dotname = '.'.join(name.parts)
+    for namemask, options in Globals.separate_options:
+        if fnmatch(dotname, namemask):
+            Globals.matched_namemasks.add(namemask)
+            new_options.MergeFrom(options)
+
+    # Handle options defined in .proto
+    if isinstance(subdesc.options, descriptor.FieldOptions):
+        ext_type = nanopb_pb2.nanopb
+    elif isinstance(subdesc.options, descriptor.FileOptions):
+        ext_type = nanopb_pb2.nanopb_fileopt
+    elif isinstance(subdesc.options, descriptor.MessageOptions):
+        ext_type = nanopb_pb2.nanopb_msgopt
+    elif isinstance(subdesc.options, descriptor.EnumOptions):
+        ext_type = nanopb_pb2.nanopb_enumopt
+    else:
+        raise Exception("Unknown options type")
+
+    if subdesc.options.HasExtension(ext_type):
+        ext = subdesc.options.Extensions[ext_type]
+        new_options.MergeFrom(ext)
+
+    if Globals.verbose_options:
+        sys.stderr.write("Options for " + dotname + ": ")
+        sys.stderr.write(text_format.MessageToString(new_options) + "\n")
+
+    return new_options
+
+
+# ---------------------------------------------------------------------------
+#                         Command line interface
+# ---------------------------------------------------------------------------
+
+import sys
+import os.path
+from optparse import OptionParser
+
+optparser = OptionParser(
+    usage = "Usage: nanopb_generator.py [options] file.pb ...",
+    epilog = "Compile file.pb from file.proto by: 'protoc -ofile.pb file.proto'. " +
+             "Output will be written to file.pb.h and file.pb.c.")
+optparser.add_option("-x", dest="exclude", metavar="FILE", action="append", default=[],
+    help="Exclude file from generated #include list.")
+optparser.add_option("-e", "--extension", dest="extension", metavar="EXTENSION", default=".pb",
+    help="Set extension to use instead of '.pb' for generated files. [default: %default]")
+optparser.add_option("-f", "--options-file", dest="options_file", metavar="FILE", default="%s.options",
+    help="Set name of a separate generator options file.")
+optparser.add_option("-I", "--options-path", dest="options_path", metavar="DIR",
+    action="append", default = [],
+    help="Search for .options files additionally in this path")
+optparser.add_option("-D", "--output-dir", dest="output_dir",
+                     metavar="OUTPUTDIR", default=None,
+                     help="Output directory of .pb.h and .pb.c files")
+optparser.add_option("-Q", "--generated-include-format", dest="genformat",
+    metavar="FORMAT", default='#include "%s"\n',
+    help="Set format string to use for including other .pb.h files. [default: %default]")
+optparser.add_option("-L", "--library-include-format", dest="libformat",
+    metavar="FORMAT", default='#include <%s>\n',
+    help="Set format string to use for including the nanopb pb.h header. [default: %default]")
+optparser.add_option("-T", "--no-timestamp", dest="notimestamp", action="store_true", default=False,
+    help="Don't add timestamp to .pb.h and .pb.c preambles")
+optparser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False,
+    help="Don't print anything except errors.")
+optparser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
+    help="Print more information.")
+optparser.add_option("-s", dest="settings", metavar="OPTION:VALUE", action="append", default=[],
+    help="Set generator option (max_size, max_count etc.).")
+
+def parse_file(filename, fdesc, options):
+    '''Parse a single file. Returns a ProtoFile instance.'''
+    toplevel_options = nanopb_pb2.NanoPBOptions()
+    for s in options.settings:
+        text_format.Merge(s, toplevel_options)
+
+    if not fdesc:
+        data = open(filename, 'rb').read()
+        fdesc = descriptor.FileDescriptorSet.FromString(data).file[0]
+
+    # Check if there is a separate .options file
+    had_abspath = False
+    try:
+        optfilename = options.options_file % os.path.splitext(filename)[0]
+    except TypeError:
+        # No %s specified, use the filename as-is
+        optfilename = options.options_file
+        had_abspath = True
+
+    paths = ['.'] + options.options_path
+    for p in paths:
+        if os.path.isfile(os.path.join(p, optfilename)):
+            optfilename = os.path.join(p, optfilename)
+            if options.verbose:
+                sys.stderr.write('Reading options from ' + optfilename + '\n')
+            Globals.separate_options = read_options_file(open(optfilename, "rU"))
+            break
+    else:
+        # If we are given a full filename and it does not exist, give an error.
+        # However, don't give error when we automatically look for .options file
+        # with the same name as .proto.
+        if options.verbose or had_abspath:
+            sys.stderr.write('Options file not found: ' + optfilename + '\n')
+        Globals.separate_options = []
+
+    Globals.matched_namemasks = set()
+
+    # Parse the file
+    file_options = get_nanopb_suboptions(fdesc, toplevel_options, Names([filename]))
+    f = ProtoFile(fdesc, file_options)
+    f.optfilename = optfilename
+
+    return f
+
+def process_file(filename, fdesc, options, other_files = {}):
+    '''Process a single file.
+    filename: The full path to the .proto or .pb source file, as string.
+    fdesc: The loaded FileDescriptorSet, or None to read from the input file.
+    options: Command line options as they come from OptionsParser.
+
+    Returns a dict:
+        {'headername': Name of header file,
+         'headerdata': Data for the .h header file,
+         'sourcename': Name of the source code file,
+         'sourcedata': Data for the .c source code file
+        }
+    '''
+    f = parse_file(filename, fdesc, options)
+
+    # Provide dependencies if available
+    for dep in f.fdesc.dependency:
+        if dep in other_files:
+            f.add_dependency(other_files[dep])
+
+    # Decide the file names
+    noext = os.path.splitext(filename)[0]
+    headername = noext + options.extension + '.h'
+    sourcename = noext + options.extension + '.c'
+    headerbasename = os.path.basename(headername)
+
+    # List of .proto files that should not be included in the C header file
+    # even if they are mentioned in the source .proto.
+    excludes = ['nanopb.proto', 'google/protobuf/descriptor.proto'] + options.exclude
+    includes = [d for d in f.fdesc.dependency if d not in excludes]
+
+    headerdata = ''.join(f.generate_header(includes, headerbasename, options))
+    sourcedata = ''.join(f.generate_source(headerbasename, options))
+
+    # Check if there were any lines in .options that did not match a member
+    unmatched = [n for n,o in Globals.separate_options if n not in Globals.matched_namemasks]
+    if unmatched and not options.quiet:
+        sys.stderr.write("Following patterns in " + f.optfilename + " did not match any fields: "
+                         + ', '.join(unmatched) + "\n")
+        if not Globals.verbose_options:
+            sys.stderr.write("Use  protoc --nanopb-out=-v:.   to see a list of the field names.\n")
+
+    return {'headername': headername, 'headerdata': headerdata,
+            'sourcename': sourcename, 'sourcedata': sourcedata}
+
+def main_cli():
+    '''Main function when invoked directly from the command line.'''
+
+    options, filenames = optparser.parse_args()
+
+    if not filenames:
+        optparser.print_help()
+        sys.exit(1)
+
+    if options.quiet:
+        options.verbose = False
+
+    if options.output_dir and not os.path.exists(options.output_dir):
+        optparser.print_help()
+        sys.stderr.write("\noutput_dir does not exist: %s\n" % options.output_dir)
+        sys.exit(1)
+
+
+    Globals.verbose_options = options.verbose
+    for filename in filenames:
+        results = process_file(filename, None, options)
+
+        base_dir = options.output_dir or ''
+        to_write = [
+            (os.path.join(base_dir, results['headername']), results['headerdata']),
+            (os.path.join(base_dir, results['sourcename']), results['sourcedata']),
+        ]
+
+        if not options.quiet:
+            paths = " and ".join([x[0] for x in to_write])
+            sys.stderr.write("Writing to %s\n" % paths)
+
+        for path, data in to_write:
+            with open(path, 'w') as f:
+                f.write(data)
+
+def main_plugin():
+    '''Main function when invoked as a protoc plugin.'''
+
+    import io, sys
+    if sys.platform == "win32":
+        import os, msvcrt
+        # Set stdin and stdout to binary mode
+        msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
+        msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
+
+    data = io.open(sys.stdin.fileno(), "rb").read()
+
+    request = plugin_pb2.CodeGeneratorRequest.FromString(data)
+
+    try:
+        # Versions of Python prior to 2.7.3 do not support unicode
+        # input to shlex.split(). Try to convert to str if possible.
+        params = str(request.parameter)
+    except UnicodeEncodeError:
+        params = request.parameter
+
+    import shlex
+    args = shlex.split(params)
+    options, dummy = optparser.parse_args(args)
+
+    Globals.verbose_options = options.verbose
+
+    response = plugin_pb2.CodeGeneratorResponse()
+
+    # Google's protoc does not currently indicate the full path of proto files.
+    # Instead always add the main file path to the search dirs, that works for
+    # the common case.
+    import os.path
+    options.options_path.append(os.path.dirname(request.file_to_generate[0]))
+
+    # Process any include files first, in order to have them
+    # available as dependencies
+    other_files = {}
+    for fdesc in request.proto_file:
+        other_files[fdesc.name] = parse_file(fdesc.name, fdesc, options)
+
+    for filename in request.file_to_generate:
+        for fdesc in request.proto_file:
+            if fdesc.name == filename:
+                results = process_file(filename, fdesc, options, other_files)
+
+                f = response.file.add()
+                f.name = results['headername']
+                f.content = results['headerdata']
+
+                f = response.file.add()
+                f.name = results['sourcename']
+                f.content = results['sourcedata']
+
+    io.open(sys.stdout.fileno(), "wb").write(response.SerializeToString())
+
+if __name__ == '__main__':
+    # Check if we are running as a plugin under protoc
+    if 'protoc-gen-' in sys.argv[0] or '--protoc-plugin' in sys.argv:
+        main_plugin()
+    else:
+        main_cli()
diff --git a/third_party/nanopb/generator/proto/Makefile b/third_party/nanopb/generator/proto/Makefile
new file mode 100644
index 0000000..89bfe52
--- /dev/null
+++ b/third_party/nanopb/generator/proto/Makefile
@@ -0,0 +1,4 @@
+all: nanopb_pb2.py plugin_pb2.py
+
+%_pb2.py: %.proto
+	protoc --python_out=. $<
diff --git a/third_party/nanopb/generator/proto/__init__.py b/third_party/nanopb/generator/proto/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/nanopb/generator/proto/__init__.py
diff --git a/third_party/nanopb/generator/proto/google/protobuf/descriptor.proto b/third_party/nanopb/generator/proto/google/protobuf/descriptor.proto
new file mode 100644
index 0000000..e17c0cc
--- /dev/null
+++ b/third_party/nanopb/generator/proto/google/protobuf/descriptor.proto
@@ -0,0 +1,714 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+//
+// The messages in this file describe the definitions found in .proto files.
+// A valid .proto file can be translated directly to a FileDescriptorProto
+// without any other information (e.g. without reading its imports).
+
+
+syntax = "proto2";
+
+package google.protobuf;
+option java_package = "com.google.protobuf";
+option java_outer_classname = "DescriptorProtos";
+
+// descriptor.proto must be optimized for speed because reflection-based
+// algorithms don't work during bootstrapping.
+option optimize_for = SPEED;
+
+// The protocol compiler can output a FileDescriptorSet containing the .proto
+// files it parses.
+message FileDescriptorSet {
+  repeated FileDescriptorProto file = 1;
+}
+
+// Describes a complete .proto file.
+message FileDescriptorProto {
+  optional string name = 1;       // file name, relative to root of source tree
+  optional string package = 2;    // e.g. "foo", "foo.bar", etc.
+
+  // Names of files imported by this file.
+  repeated string dependency = 3;
+  // Indexes of the public imported files in the dependency list above.
+  repeated int32 public_dependency = 10;
+  // Indexes of the weak imported files in the dependency list.
+  // For Google-internal migration only. Do not use.
+  repeated int32 weak_dependency = 11;
+
+  // All top-level definitions in this file.
+  repeated DescriptorProto message_type = 4;
+  repeated EnumDescriptorProto enum_type = 5;
+  repeated ServiceDescriptorProto service = 6;
+  repeated FieldDescriptorProto extension = 7;
+
+  optional FileOptions options = 8;
+
+  // This field contains optional information about the original source code.
+  // You may safely remove this entire field without harming runtime
+  // functionality of the descriptors -- the information is needed only by
+  // development tools.
+  optional SourceCodeInfo source_code_info = 9;
+
+  // The syntax of the proto file.
+  // The supported values are "proto2" and "proto3".
+  optional string syntax = 12;
+}
+
+// Describes a message type.
+message DescriptorProto {
+  optional string name = 1;
+
+  repeated FieldDescriptorProto field = 2;
+  repeated FieldDescriptorProto extension = 6;
+
+  repeated DescriptorProto nested_type = 3;
+  repeated EnumDescriptorProto enum_type = 4;
+
+  message ExtensionRange {
+    optional int32 start = 1;
+    optional int32 end = 2;
+  }
+  repeated ExtensionRange extension_range = 5;
+
+  repeated OneofDescriptorProto oneof_decl = 8;
+
+  optional MessageOptions options = 7;
+}
+
+// Describes a field within a message.
+message FieldDescriptorProto {
+  enum Type {
+    // 0 is reserved for errors.
+    // Order is weird for historical reasons.
+    TYPE_DOUBLE         = 1;
+    TYPE_FLOAT          = 2;
+    // Not ZigZag encoded.  Negative numbers take 10 bytes.  Use TYPE_SINT64 if
+    // negative values are likely.
+    TYPE_INT64          = 3;
+    TYPE_UINT64         = 4;
+    // Not ZigZag encoded.  Negative numbers take 10 bytes.  Use TYPE_SINT32 if
+    // negative values are likely.
+    TYPE_INT32          = 5;
+    TYPE_FIXED64        = 6;
+    TYPE_FIXED32        = 7;
+    TYPE_BOOL           = 8;
+    TYPE_STRING         = 9;
+    TYPE_GROUP          = 10;  // Tag-delimited aggregate.
+    TYPE_MESSAGE        = 11;  // Length-delimited aggregate.
+
+    // New in version 2.
+    TYPE_BYTES          = 12;
+    TYPE_UINT32         = 13;
+    TYPE_ENUM           = 14;
+    TYPE_SFIXED32       = 15;
+    TYPE_SFIXED64       = 16;
+    TYPE_SINT32         = 17;  // Uses ZigZag encoding.
+    TYPE_SINT64         = 18;  // Uses ZigZag encoding.
+  };
+
+  enum Label {
+    // 0 is reserved for errors
+    LABEL_OPTIONAL      = 1;
+    LABEL_REQUIRED      = 2;
+    LABEL_REPEATED      = 3;
+    // TODO(sanjay): Should we add LABEL_MAP?
+  };
+
+  optional string name = 1;
+  optional int32 number = 3;
+  optional Label label = 4;
+
+  // If type_name is set, this need not be set.  If both this and type_name
+  // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.
+  optional Type type = 5;
+
+  // For message and enum types, this is the name of the type.  If the name
+  // starts with a '.', it is fully-qualified.  Otherwise, C++-like scoping
+  // rules are used to find the type (i.e. first the nested types within this
+  // message are searched, then within the parent, on up to the root
+  // namespace).
+  optional string type_name = 6;
+
+  // For extensions, this is the name of the type being extended.  It is
+  // resolved in the same manner as type_name.
+  optional string extendee = 2;
+
+  // For numeric types, contains the original text representation of the value.
+  // For booleans, "true" or "false".
+  // For strings, contains the default text contents (not escaped in any way).
+  // For bytes, contains the C escaped value.  All bytes >= 128 are escaped.
+  // TODO(kenton):  Base-64 encode?
+  optional string default_value = 7;
+
+  // If set, gives the index of a oneof in the containing type's oneof_decl
+  // list.  This field is a member of that oneof.  Extensions of a oneof should
+  // not set this since the oneof to which they belong will be inferred based
+  // on the extension range containing the extension's field number.
+  optional int32 oneof_index = 9;
+
+  optional FieldOptions options = 8;
+}
+
+// Describes a oneof.
+message OneofDescriptorProto {
+  optional string name = 1;
+}
+
+// Describes an enum type.
+message EnumDescriptorProto {
+  optional string name = 1;
+
+  repeated EnumValueDescriptorProto value = 2;
+
+  optional EnumOptions options = 3;
+}
+
+// Describes a value within an enum.
+message EnumValueDescriptorProto {
+  optional string name = 1;
+  optional int32 number = 2;
+
+  optional EnumValueOptions options = 3;
+}
+
+// Describes a service.
+message ServiceDescriptorProto {
+  optional string name = 1;
+  repeated MethodDescriptorProto method = 2;
+
+  optional ServiceOptions options = 3;
+}
+
+// Describes a method of a service.
+message MethodDescriptorProto {
+  optional string name = 1;
+
+  // Input and output type names.  These are resolved in the same way as
+  // FieldDescriptorProto.type_name, but must refer to a message type.
+  optional string input_type = 2;
+  optional string output_type = 3;
+
+  optional MethodOptions options = 4;
+
+  // Identifies if client streams multiple client messages
+  optional bool client_streaming = 5 [default=false];
+  // Identifies if server streams multiple server messages
+  optional bool server_streaming = 6 [default=false];
+}
+
+
+// ===================================================================
+// Options
+
+// Each of the definitions above may have "options" attached.  These are
+// just annotations which may cause code to be generated slightly differently
+// or may contain hints for code that manipulates protocol messages.
+//
+// Clients may define custom options as extensions of the *Options messages.
+// These extensions may not yet be known at parsing time, so the parser cannot
+// store the values in them.  Instead it stores them in a field in the *Options
+// message called uninterpreted_option. This field must have the same name
+// across all *Options messages. We then use this field to populate the
+// extensions when we build a descriptor, at which point all protos have been
+// parsed and so all extensions are known.
+//
+// Extension numbers for custom options may be chosen as follows:
+// * For options which will only be used within a single application or
+//   organization, or for experimental options, use field numbers 50000
+//   through 99999.  It is up to you to ensure that you do not use the
+//   same number for multiple options.
+// * For options which will be published and used publicly by multiple
+//   independent entities, e-mail protobuf-global-extension-registry@google.com
+//   to reserve extension numbers. Simply provide your project name (e.g.
+//   Object-C plugin) and your porject website (if available) -- there's no need
+//   to explain how you intend to use them. Usually you only need one extension
+//   number. You can declare multiple options with only one extension number by
+//   putting them in a sub-message. See the Custom Options section of the docs
+//   for examples:
+//   https://developers.google.com/protocol-buffers/docs/proto#options
+//   If this turns out to be popular, a web service will be set up
+//   to automatically assign option numbers.
+
+
+message FileOptions {
+
+  // Sets the Java package where classes generated from this .proto will be
+  // placed.  By default, the proto package is used, but this is often
+  // inappropriate because proto packages do not normally start with backwards
+  // domain names.
+  optional string java_package = 1;
+
+
+  // If set, all the classes from the .proto file are wrapped in a single
+  // outer class with the given name.  This applies to both Proto1
+  // (equivalent to the old "--one_java_file" option) and Proto2 (where
+  // a .proto always translates to a single class, but you may want to
+  // explicitly choose the class name).
+  optional string java_outer_classname = 8;
+
+  // If set true, then the Java code generator will generate a separate .java
+  // file for each top-level message, enum, and service defined in the .proto
+  // file.  Thus, these types will *not* be nested inside the outer class
+  // named by java_outer_classname.  However, the outer class will still be
+  // generated to contain the file's getDescriptor() method as well as any
+  // top-level extensions defined in the file.
+  optional bool java_multiple_files = 10 [default=false];
+
+  // If set true, then the Java code generator will generate equals() and
+  // hashCode() methods for all messages defined in the .proto file.
+  // - In the full runtime, this is purely a speed optimization, as the
+  // AbstractMessage base class includes reflection-based implementations of
+  // these methods.
+  //- In the lite runtime, setting this option changes the semantics of
+  // equals() and hashCode() to more closely match those of the full runtime;
+  // the generated methods compute their results based on field values rather
+  // than object identity. (Implementations should not assume that hashcodes
+  // will be consistent across runtimes or versions of the protocol compiler.)
+  optional bool java_generate_equals_and_hash = 20 [default=false];
+
+  // If set true, then the Java2 code generator will generate code that
+  // throws an exception whenever an attempt is made to assign a non-UTF-8
+  // byte sequence to a string field.
+  // Message reflection will do the same.
+  // However, an extension field still accepts non-UTF-8 byte sequences.
+  // This option has no effect on when used with the lite runtime.
+  optional bool java_string_check_utf8 = 27 [default=false];
+
+
+  // Generated classes can be optimized for speed or code size.
+  enum OptimizeMode {
+    SPEED = 1;        // Generate complete code for parsing, serialization,
+                      // etc.
+    CODE_SIZE = 2;    // Use ReflectionOps to implement these methods.
+    LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
+  }
+  optional OptimizeMode optimize_for = 9 [default=SPEED];
+
+  // Sets the Go package where structs generated from this .proto will be
+  // placed. If omitted, the Go package will be derived from the following:
+  //   - The basename of the package import path, if provided.
+  //   - Otherwise, the package statement in the .proto file, if present.
+  //   - Otherwise, the basename of the .proto file, without extension.
+  optional string go_package = 11;
+
+
+
+  // Should generic services be generated in each language?  "Generic" services
+  // are not specific to any particular RPC system.  They are generated by the
+  // main code generators in each language (without additional plugins).
+  // Generic services were the only kind of service generation supported by
+  // early versions of google.protobuf.
+  //
+  // Generic services are now considered deprecated in favor of using plugins
+  // that generate code specific to your particular RPC system.  Therefore,
+  // these default to false.  Old code which depends on generic services should
+  // explicitly set them to true.
+  optional bool cc_generic_services = 16 [default=false];
+  optional bool java_generic_services = 17 [default=false];
+  optional bool py_generic_services = 18 [default=false];
+
+  // Is this file deprecated?
+  // Depending on the target platform, this can emit Deprecated annotations
+  // for everything in the file, or it will be completely ignored; in the very
+  // least, this is a formalization for deprecating files.
+  optional bool deprecated = 23 [default=false];
+
+
+  // Enables the use of arenas for the proto messages in this file. This applies
+  // only to generated classes for C++.
+  optional bool cc_enable_arenas = 31 [default=false];
+
+
+  // The parser stores options it doesn't recognize here. See above.
+  repeated UninterpretedOption uninterpreted_option = 999;
+
+  // Clients can define custom options in extensions of this message. See above.
+  extensions 1000 to max;
+}
+
+message MessageOptions {
+  // Set true to use the old proto1 MessageSet wire format for extensions.
+  // This is provided for backwards-compatibility with the MessageSet wire
+  // format.  You should not use this for any other reason:  It's less
+  // efficient, has fewer features, and is more complicated.
+  //
+  // The message must be defined exactly as follows:
+  //   message Foo {
+  //     option message_set_wire_format = true;
+  //     extensions 4 to max;
+  //   }
+  // Note that the message cannot have any defined fields; MessageSets only
+  // have extensions.
+  //
+  // All extensions of your type must be singular messages; e.g. they cannot
+  // be int32s, enums, or repeated messages.
+  //
+  // Because this is an option, the above two restrictions are not enforced by
+  // the protocol compiler.
+  optional bool message_set_wire_format = 1 [default=false];
+
+  // Disables the generation of the standard "descriptor()" accessor, which can
+  // conflict with a field of the same name.  This is meant to make migration
+  // from proto1 easier; new code should avoid fields named "descriptor".
+  optional bool no_standard_descriptor_accessor = 2 [default=false];
+
+  // Is this message deprecated?
+  // Depending on the target platform, this can emit Deprecated annotations
+  // for the message, or it will be completely ignored; in the very least,
+  // this is a formalization for deprecating messages.
+  optional bool deprecated = 3 [default=false];
+
+  // Whether the message is an automatically generated map entry type for the
+  // maps field.
+  //
+  // For maps fields:
+  //     map<KeyType, ValueType> map_field = 1;
+  // The parsed descriptor looks like:
+  //     message MapFieldEntry {
+  //         option map_entry = true;
+  //         optional KeyType key = 1;
+  //         optional ValueType value = 2;
+  //     }
+  //     repeated MapFieldEntry map_field = 1;
+  //
+  // Implementations may choose not to generate the map_entry=true message, but
+  // use a native map in the target language to hold the keys and values.
+  // The reflection APIs in such implementions still need to work as
+  // if the field is a repeated message field.
+  //
+  // NOTE: Do not set the option in .proto files. Always use the maps syntax
+  // instead. The option should only be implicitly set by the proto compiler
+  // parser.
+  optional bool map_entry = 7;
+
+  // The parser stores options it doesn't recognize here. See above.
+  repeated UninterpretedOption uninterpreted_option = 999;
+
+  // Clients can define custom options in extensions of this message. See above.
+  extensions 1000 to max;
+}
+
+message FieldOptions {
+  // The ctype option instructs the C++ code generator to use a different
+  // representation of the field than it normally would.  See the specific
+  // options below.  This option is not yet implemented in the open source
+  // release -- sorry, we'll try to include it in a future version!
+  optional CType ctype = 1 [default = STRING];
+  enum CType {
+    // Default mode.
+    STRING = 0;
+
+    CORD = 1;
+
+    STRING_PIECE = 2;
+  }
+  // The packed option can be enabled for repeated primitive fields to enable
+  // a more efficient representation on the wire. Rather than repeatedly
+  // writing the tag and type for each element, the entire array is encoded as
+  // a single length-delimited blob.
+  optional bool packed = 2;
+
+
+
+  // Should this field be parsed lazily?  Lazy applies only to message-type
+  // fields.  It means that when the outer message is initially parsed, the
+  // inner message's contents will not be parsed but instead stored in encoded
+  // form.  The inner message will actually be parsed when it is first accessed.
+  //
+  // This is only a hint.  Implementations are free to choose whether to use
+  // eager or lazy parsing regardless of the value of this option.  However,
+  // setting this option true suggests that the protocol author believes that
+  // using lazy parsing on this field is worth the additional bookkeeping
+  // overhead typically needed to implement it.
+  //
+  // This option does not affect the public interface of any generated code;
+  // all method signatures remain the same.  Furthermore, thread-safety of the
+  // interface is not affected by this option; const methods remain safe to
+  // call from multiple threads concurrently, while non-const methods continue
+  // to require exclusive access.
+  //
+  //
+  // Note that implementations may choose not to check required fields within
+  // a lazy sub-message.  That is, calling IsInitialized() on the outher message
+  // may return true even if the inner message has missing required fields.
+  // This is necessary because otherwise the inner message would have to be
+  // parsed in order to perform the check, defeating the purpose of lazy
+  // parsing.  An implementation which chooses not to check required fields
+  // must be consistent about it.  That is, for any particular sub-message, the
+  // implementation must either *always* check its required fields, or *never*
+  // check its required fields, regardless of whether or not the message has
+  // been parsed.
+  optional bool lazy = 5 [default=false];
+
+  // Is this field deprecated?
+  // Depending on the target platform, this can emit Deprecated annotations
+  // for accessors, or it will be completely ignored; in the very least, this
+  // is a formalization for deprecating fields.
+  optional bool deprecated = 3 [default=false];
+
+  // For Google-internal migration only. Do not use.
+  optional bool weak = 10 [default=false];
+
+
+
+  // The parser stores options it doesn't recognize here. See above.
+  repeated UninterpretedOption uninterpreted_option = 999;
+
+  // Clients can define custom options in extensions of this message. See above.
+  extensions 1000 to max;
+}
+
+message EnumOptions {
+
+  // Set this option to true to allow mapping different tag names to the same
+  // value.
+  optional bool allow_alias = 2;
+
+  // Is this enum deprecated?
+  // Depending on the target platform, this can emit Deprecated annotations
+  // for the enum, or it will be completely ignored; in the very least, this
+  // is a formalization for deprecating enums.
+  optional bool deprecated = 3 [default=false];
+
+  // The parser stores options it doesn't recognize here. See above.
+  repeated UninterpretedOption uninterpreted_option = 999;
+
+  // Clients can define custom options in extensions of this message. See above.
+  extensions 1000 to max;
+}
+
+message EnumValueOptions {
+  // Is this enum value deprecated?
+  // Depending on the target platform, this can emit Deprecated annotations
+  // for the enum value, or it will be completely ignored; in the very least,
+  // this is a formalization for deprecating enum values.
+  optional bool deprecated = 1 [default=false];
+
+  // The parser stores options it doesn't recognize here. See above.
+  repeated UninterpretedOption uninterpreted_option = 999;
+
+  // Clients can define custom options in extensions of this message. See above.
+  extensions 1000 to max;
+}
+
+message ServiceOptions {
+
+  // Note:  Field numbers 1 through 32 are reserved for Google's internal RPC
+  //   framework.  We apologize for hoarding these numbers to ourselves, but
+  //   we were already using them long before we decided to release Protocol
+  //   Buffers.
+
+  // Is this service deprecated?
+  // Depending on the target platform, this can emit Deprecated annotations
+  // for the service, or it will be completely ignored; in the very least,
+  // this is a formalization for deprecating services.
+  optional bool deprecated = 33 [default=false];
+
+  // The parser stores options it doesn't recognize here. See above.
+  repeated UninterpretedOption uninterpreted_option = 999;
+
+  // Clients can define custom options in extensions of this message. See above.
+  extensions 1000 to max;
+}
+
+message MethodOptions {
+
+  // Note:  Field numbers 1 through 32 are reserved for Google's internal RPC
+  //   framework.  We apologize for hoarding these numbers to ourselves, but
+  //   we were already using them long before we decided to release Protocol
+  //   Buffers.
+
+  // Is this method deprecated?
+  // Depending on the target platform, this can emit Deprecated annotations
+  // for the method, or it will be completely ignored; in the very least,
+  // this is a formalization for deprecating methods.
+  optional bool deprecated = 33 [default=false];
+
+  // The parser stores options it doesn't recognize here. See above.
+  repeated UninterpretedOption uninterpreted_option = 999;
+
+  // Clients can define custom options in extensions of this message. See above.
+  extensions 1000 to max;
+}
+
+
+// A message representing a option the parser does not recognize. This only
+// appears in options protos created by the compiler::Parser class.
+// DescriptorPool resolves these when building Descriptor objects. Therefore,
+// options protos in descriptor objects (e.g. returned by Descriptor::options(),
+// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
+// in them.
+message UninterpretedOption {
+  // The name of the uninterpreted option.  Each string represents a segment in
+  // a dot-separated name.  is_extension is true iff a segment represents an
+  // extension (denoted with parentheses in options specs in .proto files).
+  // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
+  // "foo.(bar.baz).qux".
+  message NamePart {
+    required string name_part = 1;
+    required bool is_extension = 2;
+  }
+  repeated NamePart name = 2;
+
+  // The value of the uninterpreted option, in whatever type the tokenizer
+  // identified it as during parsing. Exactly one of these should be set.
+  optional string identifier_value = 3;
+  optional uint64 positive_int_value = 4;
+  optional int64 negative_int_value = 5;
+  optional double double_value = 6;
+  optional bytes string_value = 7;
+  optional string aggregate_value = 8;
+}
+
+// ===================================================================
+// Optional source code info
+
+// Encapsulates information about the original source file from which a
+// FileDescriptorProto was generated.
+message SourceCodeInfo {
+  // A Location identifies a piece of source code in a .proto file which
+  // corresponds to a particular definition.  This information is intended
+  // to be useful to IDEs, code indexers, documentation generators, and similar
+  // tools.
+  //
+  // For example, say we have a file like:
+  //   message Foo {
+  //     optional string foo = 1;
+  //   }
+  // Let's look at just the field definition:
+  //   optional string foo = 1;
+  //   ^       ^^     ^^  ^  ^^^
+  //   a       bc     de  f  ghi
+  // We have the following locations:
+  //   span   path               represents
+  //   [a,i)  [ 4, 0, 2, 0 ]     The whole field definition.
+  //   [a,b)  [ 4, 0, 2, 0, 4 ]  The label (optional).
+  //   [c,d)  [ 4, 0, 2, 0, 5 ]  The type (string).
+  //   [e,f)  [ 4, 0, 2, 0, 1 ]  The name (foo).
+  //   [g,h)  [ 4, 0, 2, 0, 3 ]  The number (1).
+  //
+  // Notes:
+  // - A location may refer to a repeated field itself (i.e. not to any
+  //   particular index within it).  This is used whenever a set of elements are
+  //   logically enclosed in a single code segment.  For example, an entire
+  //   extend block (possibly containing multiple extension definitions) will
+  //   have an outer location whose path refers to the "extensions" repeated
+  //   field without an index.
+  // - Multiple locations may have the same path.  This happens when a single
+  //   logical declaration is spread out across multiple places.  The most
+  //   obvious example is the "extend" block again -- there may be multiple
+  //   extend blocks in the same scope, each of which will have the same path.
+  // - A location's span is not always a subset of its parent's span.  For
+  //   example, the "extendee" of an extension declaration appears at the
+  //   beginning of the "extend" block and is shared by all extensions within
+  //   the block.
+  // - Just because a location's span is a subset of some other location's span
+  //   does not mean that it is a descendent.  For example, a "group" defines
+  //   both a type and a field in a single declaration.  Thus, the locations
+  //   corresponding to the type and field and their components will overlap.
+  // - Code which tries to interpret locations should probably be designed to
+  //   ignore those that it doesn't understand, as more types of locations could
+  //   be recorded in the future.
+  repeated Location location = 1;
+  message Location {
+    // Identifies which part of the FileDescriptorProto was defined at this
+    // location.
+    //
+    // Each element is a field number or an index.  They form a path from
+    // the root FileDescriptorProto to the place where the definition.  For
+    // example, this path:
+    //   [ 4, 3, 2, 7, 1 ]
+    // refers to:
+    //   file.message_type(3)  // 4, 3
+    //       .field(7)         // 2, 7
+    //       .name()           // 1
+    // This is because FileDescriptorProto.message_type has field number 4:
+    //   repeated DescriptorProto message_type = 4;
+    // and DescriptorProto.field has field number 2:
+    //   repeated FieldDescriptorProto field = 2;
+    // and FieldDescriptorProto.name has field number 1:
+    //   optional string name = 1;
+    //
+    // Thus, the above path gives the location of a field name.  If we removed
+    // the last element:
+    //   [ 4, 3, 2, 7 ]
+    // this path refers to the whole field declaration (from the beginning
+    // of the label to the terminating semicolon).
+    repeated int32 path = 1 [packed=true];
+
+    // Always has exactly three or four elements: start line, start column,
+    // end line (optional, otherwise assumed same as start line), end column.
+    // These are packed into a single field for efficiency.  Note that line
+    // and column numbers are zero-based -- typically you will want to add
+    // 1 to each before displaying to a user.
+    repeated int32 span = 2 [packed=true];
+
+    // If this SourceCodeInfo represents a complete declaration, these are any
+    // comments appearing before and after the declaration which appear to be
+    // attached to the declaration.
+    //
+    // A series of line comments appearing on consecutive lines, with no other
+    // tokens appearing on those lines, will be treated as a single comment.
+    //
+    // Only the comment content is provided; comment markers (e.g. //) are
+    // stripped out.  For block comments, leading whitespace and an asterisk
+    // will be stripped from the beginning of each line other than the first.
+    // Newlines are included in the output.
+    //
+    // Examples:
+    //
+    //   optional int32 foo = 1;  // Comment attached to foo.
+    //   // Comment attached to bar.
+    //   optional int32 bar = 2;
+    //
+    //   optional string baz = 3;
+    //   // Comment attached to baz.
+    //   // Another line attached to baz.
+    //
+    //   // Comment attached to qux.
+    //   //
+    //   // Another line attached to qux.
+    //   optional double qux = 4;
+    //
+    //   optional string corge = 5;
+    //   /* Block comment attached
+    //    * to corge.  Leading asterisks
+    //    * will be removed. */
+    //   /* Block comment attached to
+    //    * grault. */
+    //   optional int32 grault = 6;
+    optional string leading_comments = 3;
+    optional string trailing_comments = 4;
+  }
+}
diff --git a/third_party/nanopb/generator/proto/nanopb.proto b/third_party/nanopb/generator/proto/nanopb.proto
new file mode 100644
index 0000000..8aab19a
--- /dev/null
+++ b/third_party/nanopb/generator/proto/nanopb.proto
@@ -0,0 +1,98 @@
+// Custom options for defining:
+// - Maximum size of string/bytes
+// - Maximum number of elements in array
+//
+// These are used by nanopb to generate statically allocable structures
+// for memory-limited environments.
+
+syntax = "proto2";
+import "google/protobuf/descriptor.proto";
+
+option java_package = "fi.kapsi.koti.jpa.nanopb";
+
+enum FieldType {
+    FT_DEFAULT = 0; // Automatically decide field type, generate static field if possible.
+    FT_CALLBACK = 1; // Always generate a callback field.
+    FT_POINTER = 4; // Always generate a dynamically allocated field.
+    FT_STATIC = 2; // Generate a static field or raise an exception if not possible.
+    FT_IGNORE = 3; // Ignore the field completely.
+    FT_INLINE = 5; // Always generate an inline array of fixed size.
+}
+
+enum IntSize {
+    IS_DEFAULT = 0; // Default, 32/64bit based on type in .proto
+    IS_8 = 8;
+    IS_16 = 16;
+    IS_32 = 32;
+    IS_64 = 64;
+}
+
+// This is the inner options message, which basically defines options for
+// a field. When it is used in message or file scope, it applies to all
+// fields.
+message NanoPBOptions {
+  // Allocated size for 'bytes' and 'string' fields.
+  optional int32 max_size = 1;
+  
+  // Allocated number of entries in arrays ('repeated' fields)
+  optional int32 max_count = 2;
+  
+  // Size of integer fields. Can save some memory if you don't need
+  // full 32 bits for the value.
+  optional IntSize int_size = 7 [default = IS_DEFAULT];
+
+  // Force type of field (callback or static allocation)
+  optional FieldType type = 3 [default = FT_DEFAULT];
+  
+  // Use long names for enums, i.e. EnumName_EnumValue.
+  optional bool long_names = 4 [default = true];
+  
+  // Add 'packed' attribute to generated structs.
+  // Note: this cannot be used on CPUs that break on unaligned
+  // accesses to variables.
+  optional bool packed_struct = 5 [default = false];
+  
+  // Add 'packed' attribute to generated enums.
+  optional bool packed_enum = 10 [default = false];
+  
+  // Skip this message
+  optional bool skip_message = 6 [default = false];
+
+  // Generate oneof fields as normal optional fields instead of union.
+  optional bool no_unions = 8 [default = false];
+
+  // integer type tag for a message
+  optional uint32 msgid = 9;
+
+  // decode oneof as anonymous union
+  optional bool anonymous_oneof = 11 [default = false];
+}
+
+// Extensions to protoc 'Descriptor' type in order to define options
+// inside a .proto file.
+//
+// Protocol Buffers extension number registry
+// --------------------------------
+// Project:  Nanopb
+// Contact:  Petteri Aimonen <jpa@kapsi.fi>
+// Web site: http://kapsi.fi/~jpa/nanopb
+// Extensions: 1010 (all types)
+// --------------------------------
+
+extend google.protobuf.FileOptions {
+    optional NanoPBOptions nanopb_fileopt = 1010;
+}
+
+extend google.protobuf.MessageOptions {
+    optional NanoPBOptions nanopb_msgopt = 1010;
+}
+
+extend google.protobuf.EnumOptions {
+    optional NanoPBOptions nanopb_enumopt = 1010;
+}
+
+extend google.protobuf.FieldOptions {
+    optional NanoPBOptions nanopb = 1010;
+}
+
+
diff --git a/third_party/nanopb/generator/proto/plugin.proto b/third_party/nanopb/generator/proto/plugin.proto
new file mode 100644
index 0000000..e627289
--- /dev/null
+++ b/third_party/nanopb/generator/proto/plugin.proto
@@ -0,0 +1,148 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//
+// WARNING:  The plugin interface is currently EXPERIMENTAL and is subject to
+//   change.
+//
+// protoc (aka the Protocol Compiler) can be extended via plugins.  A plugin is
+// just a program that reads a CodeGeneratorRequest from stdin and writes a
+// CodeGeneratorResponse to stdout.
+//
+// Plugins written using C++ can use google/protobuf/compiler/plugin.h instead
+// of dealing with the raw protocol defined here.
+//
+// A plugin executable needs only to be placed somewhere in the path.  The
+// plugin should be named "protoc-gen-$NAME", and will then be used when the
+// flag "--${NAME}_out" is passed to protoc.
+
+syntax = "proto2";
+package google.protobuf.compiler;
+option java_package = "com.google.protobuf.compiler";
+option java_outer_classname = "PluginProtos";
+
+import "google/protobuf/descriptor.proto";
+
+// An encoded CodeGeneratorRequest is written to the plugin's stdin.
+message CodeGeneratorRequest {
+  // The .proto files that were explicitly listed on the command-line.  The
+  // code generator should generate code only for these files.  Each file's
+  // descriptor will be included in proto_file, below.
+  repeated string file_to_generate = 1;
+
+  // The generator parameter passed on the command-line.
+  optional string parameter = 2;
+
+  // FileDescriptorProtos for all files in files_to_generate and everything
+  // they import.  The files will appear in topological order, so each file
+  // appears before any file that imports it.
+  //
+  // protoc guarantees that all proto_files will be written after
+  // the fields above, even though this is not technically guaranteed by the
+  // protobuf wire format.  This theoretically could allow a plugin to stream
+  // in the FileDescriptorProtos and handle them one by one rather than read
+  // the entire set into memory at once.  However, as of this writing, this
+  // is not similarly optimized on protoc's end -- it will store all fields in
+  // memory at once before sending them to the plugin.
+  repeated FileDescriptorProto proto_file = 15;
+}
+
+// The plugin writes an encoded CodeGeneratorResponse to stdout.
+message CodeGeneratorResponse {
+  // Error message.  If non-empty, code generation failed.  The plugin process
+  // should exit with status code zero even if it reports an error in this way.
+  //
+  // This should be used to indicate errors in .proto files which prevent the
+  // code generator from generating correct code.  Errors which indicate a
+  // problem in protoc itself -- such as the input CodeGeneratorRequest being
+  // unparseable -- should be reported by writing a message to stderr and
+  // exiting with a non-zero status code.
+  optional string error = 1;
+
+  // Represents a single generated file.
+  message File {
+    // The file name, relative to the output directory.  The name must not
+    // contain "." or ".." components and must be relative, not be absolute (so,
+    // the file cannot lie outside the output directory).  "/" must be used as
+    // the path separator, not "\".
+    //
+    // If the name is omitted, the content will be appended to the previous
+    // file.  This allows the generator to break large files into small chunks,
+    // and allows the generated text to be streamed back to protoc so that large
+    // files need not reside completely in memory at one time.  Note that as of
+    // this writing protoc does not optimize for this -- it will read the entire
+    // CodeGeneratorResponse before writing files to disk.
+    optional string name = 1;
+
+    // If non-empty, indicates that the named file should already exist, and the
+    // content here is to be inserted into that file at a defined insertion
+    // point.  This feature allows a code generator to extend the output
+    // produced by another code generator.  The original generator may provide
+    // insertion points by placing special annotations in the file that look
+    // like:
+    //   @@protoc_insertion_point(NAME)
+    // The annotation can have arbitrary text before and after it on the line,
+    // which allows it to be placed in a comment.  NAME should be replaced with
+    // an identifier naming the point -- this is what other generators will use
+    // as the insertion_point.  Code inserted at this point will be placed
+    // immediately above the line containing the insertion point (thus multiple
+    // insertions to the same point will come out in the order they were added).
+    // The double-@ is intended to make it unlikely that the generated code
+    // could contain things that look like insertion points by accident.
+    //
+    // For example, the C++ code generator places the following line in the
+    // .pb.h files that it generates:
+    //   // @@protoc_insertion_point(namespace_scope)
+    // This line appears within the scope of the file's package namespace, but
+    // outside of any particular class.  Another plugin can then specify the
+    // insertion_point "namespace_scope" to generate additional classes or
+    // other declarations that should be placed in this scope.
+    //
+    // Note that if the line containing the insertion point begins with
+    // whitespace, the same whitespace will be added to every line of the
+    // inserted text.  This is useful for languages like Python, where
+    // indentation matters.  In these languages, the insertion point comment
+    // should be indented the same amount as any inserted code will need to be
+    // in order to work correctly in that context.
+    //
+    // The code generator that generates the initial file and the one which
+    // inserts into it must both run as part of a single invocation of protoc.
+    // Code generators are executed in the order in which they appear on the
+    // command line.
+    //
+    // If |insertion_point| is present, |name| must also be present.
+    optional string insertion_point = 2;
+
+    // The file contents.
+    optional string content = 15;
+  }
+  repeated File file = 15;
+}
diff --git a/third_party/nanopb/generator/protoc-gen-nanopb b/third_party/nanopb/generator/protoc-gen-nanopb
new file mode 100755
index 0000000..358f97c
--- /dev/null
+++ b/third_party/nanopb/generator/protoc-gen-nanopb
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# This file is used to invoke nanopb_generator.py as a plugin
+# to protoc on Linux and other *nix-style systems.
+# Use it like this:
+# protoc --plugin=nanopb=..../protoc-gen-nanopb --nanopb_out=dir foo.proto
+#
+# Note that if you use the binary package of nanopb, the protoc
+# path is already set up properly and there is no need to give
+# --plugin= on the command line.
+
+MYPATH=$(dirname "$0")
+exec "$MYPATH/nanopb_generator.py" --protoc-plugin
diff --git a/third_party/nanopb/generator/protoc-gen-nanopb.bat b/third_party/nanopb/generator/protoc-gen-nanopb.bat
new file mode 100644
index 0000000..7624984
--- /dev/null
+++ b/third_party/nanopb/generator/protoc-gen-nanopb.bat
@@ -0,0 +1,12 @@
+@echo off
+:: This file is used to invoke nanopb_generator.py as a plugin
+:: to protoc on Windows.
+:: Use it like this:
+:: protoc --plugin=nanopb=..../protoc-gen-nanopb.bat --nanopb_out=dir foo.proto
+::
+:: Note that if you use the binary package of nanopb, the protoc
+:: path is already set up properly and there is no need to give
+:: --plugin= on the command line.
+
+set mydir=%~dp0
+python "%mydir%\nanopb_generator.py" --protoc-plugin
diff --git a/third_party/nanopb/library.json b/third_party/nanopb/library.json
new file mode 100644
index 0000000..30a56c9
--- /dev/null
+++ b/third_party/nanopb/library.json
@@ -0,0 +1,22 @@
+{
+  "name": "Nanopb",
+  "keywords": "protocol buffers, protobuf, google",
+  "description": "Nanopb is a plain-C implementation of Google's Protocol Buffers data format. It is targeted at 32 bit microcontrollers, but is also fit for other embedded systems with tight (2-10 kB ROM, <1 kB RAM) memory constraints.",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/nanopb/nanopb.git"
+  },
+  "authors": [{
+    "name": "Petteri Aimonen",
+    "email": "jpa@nanopb.mail.kapsi.fi",
+    "url": "http://koti.kapsi.fi/jpa/nanopb/"
+  }],
+  "include": [
+    "*.c",
+    "*.cpp",
+    "*.h"
+  ],
+  "examples": "examples/*/*.c",
+  "frameworks": "*",
+  "platforms": "*"
+}
diff --git a/third_party/nanopb/pb.h b/third_party/nanopb/pb.h
new file mode 100644
index 0000000..4576f79
--- /dev/null
+++ b/third_party/nanopb/pb.h
@@ -0,0 +1,579 @@
+/* Common parts of the nanopb library. Most of these are quite low-level
+ * stuff. For the high-level interface, see pb_encode.h and pb_decode.h.
+ */
+
+#ifndef PB_H_INCLUDED
+#define PB_H_INCLUDED
+
+/*****************************************************************
+ * Nanopb compilation time options. You can change these here by *
+ * uncommenting the lines, or on the compiler command line.      *
+ *****************************************************************/
+
+/* Enable support for dynamically allocated fields */
+/* #define PB_ENABLE_MALLOC 1 */
+
+/* Define this if your CPU / compiler combination does not support
+ * unaligned memory access to packed structures. */
+/* #define PB_NO_PACKED_STRUCTS 1 */
+
+/* Increase the number of required fields that are tracked.
+ * A compiler warning will tell if you need this. */
+/* #define PB_MAX_REQUIRED_FIELDS 256 */
+
+/* Add support for tag numbers > 255 and fields larger than 255 bytes. */
+/* #define PB_FIELD_16BIT 1 */
+
+/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */
+/* #define PB_FIELD_32BIT 1 */
+
+/* Disable support for error messages in order to save some code space. */
+/* #define PB_NO_ERRMSG 1 */
+
+/* Disable support for custom streams (support only memory buffers). */
+/* #define PB_BUFFER_ONLY 1 */
+
+/* Switch back to the old-style callback function signature.
+ * This was the default until nanopb-0.2.1. */
+/* #define PB_OLD_CALLBACK_STYLE */
+
+
+/******************************************************************
+ * You usually don't need to change anything below this line.     *
+ * Feel free to look around and use the defined macros, though.   *
+ ******************************************************************/
+
+
+/* Version of the nanopb library. Just in case you want to check it in
+ * your own program. */
+#define NANOPB_VERSION nanopb-0.3.7-dev
+
+/* Include all the system headers needed by nanopb. You will need the
+ * definitions of the following:
+ * - strlen, memcpy, memset functions
+ * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t
+ * - size_t
+ * - bool
+ *
+ * If you don't have the standard header files, you can instead provide
+ * a custom header that defines or includes all this. In that case,
+ * define PB_SYSTEM_HEADER to the path of this file.
+ */
+#ifdef PB_SYSTEM_HEADER
+#include PB_SYSTEM_HEADER
+#else
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+
+#ifdef PB_ENABLE_MALLOC
+#include <stdlib.h>
+#endif
+#endif
+
+/* Macro for defining packed structures (compiler dependent).
+ * This just reduces memory requirements, but is not required.
+ */
+#if defined(PB_NO_PACKED_STRUCTS)
+    /* Disable struct packing */
+#   define PB_PACKED_STRUCT_START
+#   define PB_PACKED_STRUCT_END
+#   define pb_packed
+#elif defined(__GNUC__) || defined(__clang__)
+    /* For GCC and clang */
+#   define PB_PACKED_STRUCT_START
+#   define PB_PACKED_STRUCT_END
+#   define pb_packed __attribute__((packed))
+#elif defined(__ICCARM__) || defined(__CC_ARM)
+    /* For IAR ARM and Keil MDK-ARM compilers */
+#   define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)")
+#   define PB_PACKED_STRUCT_END _Pragma("pack(pop)")
+#   define pb_packed
+#elif defined(_MSC_VER) && (_MSC_VER >= 1500)
+    /* For Microsoft Visual C++ */
+#   define PB_PACKED_STRUCT_START __pragma(pack(push, 1))
+#   define PB_PACKED_STRUCT_END __pragma(pack(pop))
+#   define pb_packed
+#else
+    /* Unknown compiler */
+#   define PB_PACKED_STRUCT_START
+#   define PB_PACKED_STRUCT_END
+#   define pb_packed
+#endif
+
+/* Handly macro for suppressing unreferenced-parameter compiler warnings. */
+#ifndef PB_UNUSED
+#define PB_UNUSED(x) (void)(x)
+#endif
+
+/* Compile-time assertion, used for checking compatible compilation options.
+ * If this does not work properly on your compiler, use
+ * #define PB_NO_STATIC_ASSERT to disable it.
+ *
+ * But before doing that, check carefully the error message / place where it
+ * comes from to see if the error has a real cause. Unfortunately the error
+ * message is not always very clear to read, but you can see the reason better
+ * in the place where the PB_STATIC_ASSERT macro was called.
+ */
+#ifndef PB_NO_STATIC_ASSERT
+#ifndef PB_STATIC_ASSERT
+#define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1];
+#define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER)
+#define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##LINE##COUNTER
+#endif
+#else
+#define PB_STATIC_ASSERT(COND,MSG)
+#endif
+
+/* Number of required fields to keep track of. */
+#ifndef PB_MAX_REQUIRED_FIELDS
+#define PB_MAX_REQUIRED_FIELDS 64
+#endif
+
+#if PB_MAX_REQUIRED_FIELDS < 64
+#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64).
+#endif
+
+/* List of possible field types. These are used in the autogenerated code.
+ * Least-significant 4 bits tell the scalar type
+ * Most-significant 4 bits specify repeated/required/packed etc.
+ */
+
+typedef uint_least8_t pb_type_t;
+
+/**** Field data types ****/
+
+/* Numeric types */
+#define PB_LTYPE_VARINT  0x00 /* int32, int64, enum, bool */
+#define PB_LTYPE_UVARINT 0x01 /* uint32, uint64 */
+#define PB_LTYPE_SVARINT 0x02 /* sint32, sint64 */
+#define PB_LTYPE_FIXED32 0x03 /* fixed32, sfixed32, float */
+#define PB_LTYPE_FIXED64 0x04 /* fixed64, sfixed64, double */
+
+/* Marker for last packable field type. */
+#define PB_LTYPE_LAST_PACKABLE 0x04
+
+/* Byte array with pre-allocated buffer.
+ * data_size is the length of the allocated PB_BYTES_ARRAY structure. */
+#define PB_LTYPE_BYTES 0x05
+
+/* String with pre-allocated buffer.
+ * data_size is the maximum length. */
+#define PB_LTYPE_STRING 0x06
+
+/* Submessage
+ * submsg_fields is pointer to field descriptions */
+#define PB_LTYPE_SUBMESSAGE 0x07
+
+/* Extension pseudo-field
+ * The field contains a pointer to pb_extension_t */
+#define PB_LTYPE_EXTENSION 0x08
+
+/* Byte array with inline, pre-allocated byffer.
+ * data_size is the length of the inline, allocated buffer.
+ * This differs from PB_LTYPE_BYTES by defining the element as
+ * pb_byte_t[data_size] rather than pb_bytes_array_t. */
+#define PB_LTYPE_FIXED_LENGTH_BYTES 0x09
+
+/* Number of declared LTYPES */
+#define PB_LTYPES_COUNT 0x0A
+#define PB_LTYPE_MASK 0x0F
+
+/**** Field repetition rules ****/
+
+#define PB_HTYPE_REQUIRED 0x00
+#define PB_HTYPE_OPTIONAL 0x10
+#define PB_HTYPE_REPEATED 0x20
+#define PB_HTYPE_ONEOF    0x30
+#define PB_HTYPE_MASK     0x30
+
+/**** Field allocation types ****/
+ 
+#define PB_ATYPE_STATIC   0x00
+#define PB_ATYPE_POINTER  0x80
+#define PB_ATYPE_CALLBACK 0x40
+#define PB_ATYPE_MASK     0xC0
+
+#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK)
+#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK)
+#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK)
+
+/* Data type used for storing sizes of struct fields
+ * and array counts.
+ */
+#if defined(PB_FIELD_32BIT)
+    typedef uint32_t pb_size_t;
+    typedef int32_t pb_ssize_t;
+#elif defined(PB_FIELD_16BIT)
+    typedef uint_least16_t pb_size_t;
+    typedef int_least16_t pb_ssize_t;
+#else
+    typedef uint_least8_t pb_size_t;
+    typedef int_least8_t pb_ssize_t;
+#endif
+#define PB_SIZE_MAX ((pb_size_t)-1)
+
+/* Data type for storing encoded data and other byte streams.
+ * This typedef exists to support platforms where uint8_t does not exist.
+ * You can regard it as equivalent on uint8_t on other platforms.
+ */
+typedef uint_least8_t pb_byte_t;
+
+/* This structure is used in auto-generated constants
+ * to specify struct fields.
+ * You can change field sizes if you need structures
+ * larger than 256 bytes or field tags larger than 256.
+ * The compiler should complain if your .proto has such
+ * structures. Fix that by defining PB_FIELD_16BIT or
+ * PB_FIELD_32BIT.
+ */
+PB_PACKED_STRUCT_START
+typedef struct pb_field_s pb_field_t;
+struct pb_field_s {
+    pb_size_t tag;
+    pb_type_t type;
+    pb_size_t data_offset; /* Offset of field data, relative to previous field. */
+    pb_ssize_t size_offset; /* Offset of array size or has-boolean, relative to data */
+    pb_size_t data_size; /* Data size in bytes for a single item */
+    pb_size_t array_size; /* Maximum number of entries in array */
+    
+    /* Field definitions for submessage
+     * OR default value for all other non-array, non-callback types
+     * If null, then field will zeroed. */
+    const void *ptr;
+} pb_packed;
+PB_PACKED_STRUCT_END
+
+/* Make sure that the standard integer types are of the expected sizes.
+ * Otherwise fixed32/fixed64 fields can break.
+ *
+ * If you get errors here, it probably means that your stdint.h is not
+ * correct for your platform.
+ */
+PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE)
+PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE)
+
+/* This structure is used for 'bytes' arrays.
+ * It has the number of bytes in the beginning, and after that an array.
+ * Note that actual structs used will have a different length of bytes array.
+ */
+#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; }
+#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes))
+
+struct pb_bytes_array_s {
+    pb_size_t size;
+    pb_byte_t bytes[1];
+};
+typedef struct pb_bytes_array_s pb_bytes_array_t;
+
+/* This structure is used for giving the callback function.
+ * It is stored in the message structure and filled in by the method that
+ * calls pb_decode.
+ *
+ * The decoding callback will be given a limited-length stream
+ * If the wire type was string, the length is the length of the string.
+ * If the wire type was a varint/fixed32/fixed64, the length is the length
+ * of the actual value.
+ * The function may be called multiple times (especially for repeated types,
+ * but also otherwise if the message happens to contain the field multiple
+ * times.)
+ *
+ * The encoding callback will receive the actual output stream.
+ * It should write all the data in one call, including the field tag and
+ * wire type. It can write multiple fields.
+ *
+ * The callback can be null if you want to skip a field.
+ */
+typedef struct pb_istream_s pb_istream_t;
+typedef struct pb_ostream_s pb_ostream_t;
+typedef struct pb_callback_s pb_callback_t;
+struct pb_callback_s {
+#ifdef PB_OLD_CALLBACK_STYLE
+    /* Deprecated since nanopb-0.2.1 */
+    union {
+        bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void *arg);
+        bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, const void *arg);
+    } funcs;
+#else
+    /* New function signature, which allows modifying arg contents in callback. */
+    union {
+        bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
+        bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
+    } funcs;
+#endif    
+    
+    /* Free arg for use by callback */
+    void *arg;
+};
+
+/* Wire types. Library user needs these only in encoder callbacks. */
+typedef enum {
+    PB_WT_VARINT = 0,
+    PB_WT_64BIT  = 1,
+    PB_WT_STRING = 2,
+    PB_WT_32BIT  = 5
+} pb_wire_type_t;
+
+/* Structure for defining the handling of unknown/extension fields.
+ * Usually the pb_extension_type_t structure is automatically generated,
+ * while the pb_extension_t structure is created by the user. However,
+ * if you want to catch all unknown fields, you can also create a custom
+ * pb_extension_type_t with your own callback.
+ */
+typedef struct pb_extension_type_s pb_extension_type_t;
+typedef struct pb_extension_s pb_extension_t;
+struct pb_extension_type_s {
+    /* Called for each unknown field in the message.
+     * If you handle the field, read off all of its data and return true.
+     * If you do not handle the field, do not read anything and return true.
+     * If you run into an error, return false.
+     * Set to NULL for default handler.
+     */
+    bool (*decode)(pb_istream_t *stream, pb_extension_t *extension,
+                   uint32_t tag, pb_wire_type_t wire_type);
+    
+    /* Called once after all regular fields have been encoded.
+     * If you have something to write, do so and return true.
+     * If you do not have anything to write, just return true.
+     * If you run into an error, return false.
+     * Set to NULL for default handler.
+     */
+    bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension);
+    
+    /* Free field for use by the callback. */
+    const void *arg;
+};
+
+struct pb_extension_s {
+    /* Type describing the extension field. Usually you'll initialize
+     * this to a pointer to the automatically generated structure. */
+    const pb_extension_type_t *type;
+    
+    /* Destination for the decoded data. This must match the datatype
+     * of the extension field. */
+    void *dest;
+    
+    /* Pointer to the next extension handler, or NULL.
+     * If this extension does not match a field, the next handler is
+     * automatically called. */
+    pb_extension_t *next;
+
+    /* The decoder sets this to true if the extension was found.
+     * Ignored for encoding. */
+    bool found;
+};
+
+/* Memory allocation functions to use. You can define pb_realloc and
+ * pb_free to custom functions if you want. */
+#ifdef PB_ENABLE_MALLOC
+#   ifndef pb_realloc
+#       define pb_realloc(ptr, size) realloc(ptr, size)
+#   endif
+#   ifndef pb_free
+#       define pb_free(ptr) free(ptr)
+#   endif
+#endif
+
+/* This is used to inform about need to regenerate .pb.h/.pb.c files. */
+#define PB_PROTO_HEADER_VERSION 30
+
+/* These macros are used to declare pb_field_t's in the constant array. */
+/* Size of a structure member, in bytes. */
+#define pb_membersize(st, m) (sizeof ((st*)0)->m)
+/* Number of entries in an array. */
+#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0]))
+/* Delta from start of one member to the start of another member. */
+#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2))
+/* Marks the end of the field list */
+#define PB_LAST_FIELD {0,(pb_type_t) 0,0,0,0,0,0}
+
+/* Macros for filling in the data_offset field */
+/* data_offset for first field in a message */
+#define PB_DATAOFFSET_FIRST(st, m1, m2) (offsetof(st, m1))
+/* data_offset for subsequent fields */
+#define PB_DATAOFFSET_OTHER(st, m1, m2) (offsetof(st, m1) - offsetof(st, m2) - pb_membersize(st, m2))
+/* Choose first/other based on m1 == m2 (deprecated, remains for backwards compatibility) */
+#define PB_DATAOFFSET_CHOOSE(st, m1, m2) (int)(offsetof(st, m1) == offsetof(st, m2) \
+                                  ? PB_DATAOFFSET_FIRST(st, m1, m2) \
+                                  : PB_DATAOFFSET_OTHER(st, m1, m2))
+
+/* Required fields are the simplest. They just have delta (padding) from
+ * previous field end, and the size of the field. Pointer is used for
+ * submessages and default values.
+ */
+#define PB_REQUIRED_STATIC(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_REQUIRED | ltype, \
+    fd, 0, pb_membersize(st, m), 0, ptr}
+
+/* Optional fields add the delta to the has_ variable. */
+#define PB_OPTIONAL_STATIC(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, \
+    fd, \
+    pb_delta(st, has_ ## m, m), \
+    pb_membersize(st, m), 0, ptr}
+
+/* Repeated fields have a _count field and also the maximum number of entries. */
+#define PB_REPEATED_STATIC(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_REPEATED | ltype, \
+    fd, \
+    pb_delta(st, m ## _count, m), \
+    pb_membersize(st, m[0]), \
+    pb_arraysize(st, m), ptr}
+
+#define PB_REQUIRED_INLINE(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_REQUIRED | PB_LTYPE_FIXED_LENGTH_BYTES, \
+    fd, 0, pb_membersize(st, m), 0, ptr}
+
+/* Optional fields add the delta to the has_ variable. */
+#define PB_OPTIONAL_INLINE(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | PB_LTYPE_FIXED_LENGTH_BYTES, \
+    fd, \
+    pb_delta(st, has_ ## m, m), \
+    pb_membersize(st, m), 0, ptr}
+
+/* INLINE does not support REPEATED fields. */
+
+/* Allocated fields carry the size of the actual data, not the pointer */
+#define PB_REQUIRED_POINTER(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_POINTER | PB_HTYPE_REQUIRED | ltype, \
+    fd, 0, pb_membersize(st, m[0]), 0, ptr}
+
+/* Optional fields don't need a has_ variable, as information would be redundant */
+#define PB_OPTIONAL_POINTER(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_POINTER | PB_HTYPE_OPTIONAL | ltype, \
+    fd, 0, pb_membersize(st, m[0]), 0, ptr}
+
+/* Repeated fields have a _count field and a pointer to array of pointers */
+#define PB_REPEATED_POINTER(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_POINTER | PB_HTYPE_REPEATED | ltype, \
+    fd, pb_delta(st, m ## _count, m), \
+    pb_membersize(st, m[0]), 0, ptr}
+
+/* Callbacks are much like required fields except with special datatype. */
+#define PB_REQUIRED_CALLBACK(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_CALLBACK | PB_HTYPE_REQUIRED | ltype, \
+    fd, 0, pb_membersize(st, m), 0, ptr}
+
+#define PB_OPTIONAL_CALLBACK(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, \
+    fd, 0, pb_membersize(st, m), 0, ptr}
+    
+#define PB_REPEATED_CALLBACK(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_CALLBACK | PB_HTYPE_REPEATED | ltype, \
+    fd, 0, pb_membersize(st, m), 0, ptr}
+
+/* Optional extensions don't have the has_ field, as that would be redundant. */
+#define PB_OPTEXT_STATIC(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, \
+    0, \
+    0, \
+    pb_membersize(st, m), 0, ptr}
+
+#define PB_OPTEXT_POINTER(tag, st, m, fd, ltype, ptr) \
+    PB_OPTIONAL_POINTER(tag, st, m, fd, ltype, ptr)
+
+/* INLINE does not support OPTEXT. */
+
+#define PB_OPTEXT_CALLBACK(tag, st, m, fd, ltype, ptr) \
+    PB_OPTIONAL_CALLBACK(tag, st, m, fd, ltype, ptr)
+
+/* The mapping from protobuf types to LTYPEs is done using these macros. */
+#define PB_LTYPE_MAP_BOOL       PB_LTYPE_VARINT
+#define PB_LTYPE_MAP_BYTES      PB_LTYPE_BYTES
+#define PB_LTYPE_MAP_DOUBLE     PB_LTYPE_FIXED64
+#define PB_LTYPE_MAP_ENUM       PB_LTYPE_VARINT
+#define PB_LTYPE_MAP_UENUM      PB_LTYPE_UVARINT
+#define PB_LTYPE_MAP_FIXED32    PB_LTYPE_FIXED32
+#define PB_LTYPE_MAP_FIXED64    PB_LTYPE_FIXED64
+#define PB_LTYPE_MAP_FLOAT      PB_LTYPE_FIXED32
+#define PB_LTYPE_MAP_INT32      PB_LTYPE_VARINT
+#define PB_LTYPE_MAP_INT64      PB_LTYPE_VARINT
+#define PB_LTYPE_MAP_MESSAGE    PB_LTYPE_SUBMESSAGE
+#define PB_LTYPE_MAP_SFIXED32   PB_LTYPE_FIXED32
+#define PB_LTYPE_MAP_SFIXED64   PB_LTYPE_FIXED64
+#define PB_LTYPE_MAP_SINT32     PB_LTYPE_SVARINT
+#define PB_LTYPE_MAP_SINT64     PB_LTYPE_SVARINT
+#define PB_LTYPE_MAP_STRING     PB_LTYPE_STRING
+#define PB_LTYPE_MAP_UINT32     PB_LTYPE_UVARINT
+#define PB_LTYPE_MAP_UINT64     PB_LTYPE_UVARINT
+#define PB_LTYPE_MAP_EXTENSION  PB_LTYPE_EXTENSION
+
+/* This is the actual macro used in field descriptions.
+ * It takes these arguments:
+ * - Field tag number
+ * - Field type:   BOOL, BYTES, DOUBLE, ENUM, UENUM, FIXED32, FIXED64,
+ *                 FLOAT, INT32, INT64, MESSAGE, SFIXED32, SFIXED64
+ *                 SINT32, SINT64, STRING, UINT32, UINT64 or EXTENSION
+ * - Field rules:  REQUIRED, OPTIONAL or REPEATED
+ * - Allocation:   STATIC, INLINE, or CALLBACK
+ * - Placement: FIRST or OTHER, depending on if this is the first field in structure.
+ * - Message name
+ * - Field name
+ * - Previous field name (or field name again for first field)
+ * - Pointer to default value or submsg fields.
+ */
+
+#define PB_FIELD(tag, type, rules, allocation, placement, message, field, prevfield, ptr) \
+        PB_ ## rules ## _ ## allocation(tag, message, field, \
+        PB_DATAOFFSET_ ## placement(message, field, prevfield), \
+        PB_LTYPE_MAP_ ## type, ptr)
+
+/* Field description for oneof fields. This requires taking into account the
+ * union name also, that's why a separate set of macros is needed.
+ */
+#define PB_ONEOF_STATIC(u, tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_ONEOF | ltype, \
+    fd, pb_delta(st, which_ ## u, u.m), \
+    pb_membersize(st, u.m), 0, ptr}
+
+#define PB_ONEOF_POINTER(u, tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_POINTER | PB_HTYPE_ONEOF | ltype, \
+    fd, pb_delta(st, which_ ## u, u.m), \
+    pb_membersize(st, u.m[0]), 0, ptr}
+
+/* INLINE does not support ONEOF. */
+
+#define PB_ONEOF_FIELD(union_name, tag, type, rules, allocation, placement, message, field, prevfield, ptr) \
+        PB_ONEOF_ ## allocation(union_name, tag, message, field, \
+        PB_DATAOFFSET_ ## placement(message, union_name.field, prevfield), \
+        PB_LTYPE_MAP_ ## type, ptr)
+
+#define PB_ANONYMOUS_ONEOF_STATIC(u, tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_ONEOF | ltype, \
+    fd, pb_delta(st, which_ ## u, m), \
+    pb_membersize(st, m), 0, ptr}
+
+#define PB_ANONYMOUS_ONEOF_POINTER(u, tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_POINTER | PB_HTYPE_ONEOF | ltype, \
+    fd, pb_delta(st, which_ ## u, m), \
+    pb_membersize(st, m[0]), 0, ptr}
+
+#define PB_ANONYMOUS_ONEOF_FIELD(union_name, tag, type, rules, allocation, placement, message, field, prevfield, ptr) \
+        PB_ANONYMOUS_ONEOF_ ## allocation(union_name, tag, message, field, \
+        PB_DATAOFFSET_ ## placement(message, field, prevfield), \
+        PB_LTYPE_MAP_ ## type, ptr)
+
+/* These macros are used for giving out error messages.
+ * They are mostly a debugging aid; the main error information
+ * is the true/false return value from functions.
+ * Some code space can be saved by disabling the error
+ * messages if not used.
+ *
+ * PB_SET_ERROR() sets the error message if none has been set yet.
+ *                msg must be a constant string literal.
+ * PB_GET_ERROR() always returns a pointer to a string.
+ * PB_RETURN_ERROR() sets the error and returns false from current
+ *                   function.
+ */
+#ifdef PB_NO_ERRMSG
+#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream)
+#define PB_GET_ERROR(stream) "(errmsg disabled)"
+#else
+#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg))
+#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)")
+#endif
+
+#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false
+
+#endif
diff --git a/third_party/nanopb/pb_common.c b/third_party/nanopb/pb_common.c
new file mode 100644
index 0000000..385c019
--- /dev/null
+++ b/third_party/nanopb/pb_common.c
@@ -0,0 +1,97 @@
+/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c.
+ *
+ * 2014 Petteri Aimonen <jpa@kapsi.fi>
+ */
+
+#include "pb_common.h"
+
+bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_field_t *fields, void *dest_struct)
+{
+    iter->start = fields;
+    iter->pos = fields;
+    iter->required_field_index = 0;
+    iter->dest_struct = dest_struct;
+    iter->pData = (char*)dest_struct + iter->pos->data_offset;
+    iter->pSize = (char*)iter->pData + iter->pos->size_offset;
+    
+    return (iter->pos->tag != 0);
+}
+
+bool pb_field_iter_next(pb_field_iter_t *iter)
+{
+    const pb_field_t *prev_field = iter->pos;
+
+    if (prev_field->tag == 0)
+    {
+        /* Handle empty message types, where the first field is already the terminator.
+         * In other cases, the iter->pos never points to the terminator. */
+        return false;
+    }
+    
+    iter->pos++;
+    
+    if (iter->pos->tag == 0)
+    {
+        /* Wrapped back to beginning, reinitialize */
+        (void)pb_field_iter_begin(iter, iter->start, iter->dest_struct);
+        return false;
+    }
+    else
+    {
+        /* Increment the pointers based on previous field size */
+        size_t prev_size = prev_field->data_size;
+    
+        if (PB_HTYPE(prev_field->type) == PB_HTYPE_ONEOF &&
+            PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF)
+        {
+            /* Don't advance pointers inside unions */
+            prev_size = 0;
+            iter->pData = (char*)iter->pData - prev_field->data_offset;
+        }
+        else if (PB_ATYPE(prev_field->type) == PB_ATYPE_STATIC &&
+                 PB_HTYPE(prev_field->type) == PB_HTYPE_REPEATED)
+        {
+            /* In static arrays, the data_size tells the size of a single entry and
+             * array_size is the number of entries */
+            prev_size *= prev_field->array_size;
+        }
+        else if (PB_ATYPE(prev_field->type) == PB_ATYPE_POINTER)
+        {
+            /* Pointer fields always have a constant size in the main structure.
+             * The data_size only applies to the dynamically allocated area. */
+            prev_size = sizeof(void*);
+        }
+
+        if (PB_HTYPE(prev_field->type) == PB_HTYPE_REQUIRED)
+        {
+            /* Count the required fields, in order to check their presence in the
+             * decoder. */
+            iter->required_field_index++;
+        }
+    
+        iter->pData = (char*)iter->pData + prev_size + iter->pos->data_offset;
+        iter->pSize = (char*)iter->pData + iter->pos->size_offset;
+        return true;
+    }
+}
+
+bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag)
+{
+    const pb_field_t *start = iter->pos;
+    
+    do {
+        if (iter->pos->tag == tag &&
+            PB_LTYPE(iter->pos->type) != PB_LTYPE_EXTENSION)
+        {
+            /* Found the wanted field */
+            return true;
+        }
+        
+        (void)pb_field_iter_next(iter);
+    } while (iter->pos != start);
+    
+    /* Searched all the way back to start, and found nothing. */
+    return false;
+}
+
+
diff --git a/third_party/nanopb/pb_common.h b/third_party/nanopb/pb_common.h
new file mode 100644
index 0000000..60b3d37
--- /dev/null
+++ b/third_party/nanopb/pb_common.h
@@ -0,0 +1,42 @@
+/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c.
+ * These functions are rarely needed by applications directly.
+ */
+
+#ifndef PB_COMMON_H_INCLUDED
+#define PB_COMMON_H_INCLUDED
+
+#include "pb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Iterator for pb_field_t list */
+struct pb_field_iter_s {
+    const pb_field_t *start;       /* Start of the pb_field_t array */
+    const pb_field_t *pos;         /* Current position of the iterator */
+    unsigned required_field_index; /* Zero-based index that counts only the required fields */
+    void *dest_struct;             /* Pointer to start of the structure */
+    void *pData;                   /* Pointer to current field value */
+    void *pSize;                   /* Pointer to count/has field */
+};
+typedef struct pb_field_iter_s pb_field_iter_t;
+
+/* Initialize the field iterator structure to beginning.
+ * Returns false if the message type is empty. */
+bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_field_t *fields, void *dest_struct);
+
+/* Advance the iterator to the next field.
+ * Returns false when the iterator wraps back to the first field. */
+bool pb_field_iter_next(pb_field_iter_t *iter);
+
+/* Advance the iterator until it points at a field with the given tag.
+ * Returns false if no such field exists. */
+bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
+
diff --git a/third_party/nanopb/pb_decode.c b/third_party/nanopb/pb_decode.c
new file mode 100644
index 0000000..7a4e29a
--- /dev/null
+++ b/third_party/nanopb/pb_decode.c
@@ -0,0 +1,1347 @@
+/* pb_decode.c -- decode a protobuf using minimal resources
+ *
+ * 2011 Petteri Aimonen <jpa@kapsi.fi>
+ */
+
+/* Use the GCC warn_unused_result attribute to check that all return values
+ * are propagated correctly. On other compilers and gcc before 3.4.0 just
+ * ignore the annotation.
+ */
+#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
+    #define checkreturn
+#else
+    #define checkreturn __attribute__((warn_unused_result))
+#endif
+
+#include "pb.h"
+#include "pb_decode.h"
+#include "pb_common.h"
+
+/**************************************
+ * Declarations internal to this file *
+ **************************************/
+
+typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest) checkreturn;
+
+static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);
+static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest);
+static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size);
+static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter);
+static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter);
+static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter);
+static void iter_from_extension(pb_field_iter_t *iter, pb_extension_t *extension);
+static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type);
+static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter);
+static bool checkreturn find_extension_field(pb_field_iter_t *iter);
+static void pb_field_set_to_default(pb_field_iter_t *iter);
+static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct);
+static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_skip_varint(pb_istream_t *stream);
+static bool checkreturn pb_skip_string(pb_istream_t *stream);
+
+#ifdef PB_ENABLE_MALLOC
+static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size);
+static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter);
+static void pb_release_single_field(const pb_field_iter_t *iter);
+#endif
+
+/* --- Function pointers to field decoders ---
+ * Order in the array must match pb_action_t LTYPE numbering.
+ */
+static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = {
+    &pb_dec_varint,
+    &pb_dec_uvarint,
+    &pb_dec_svarint,
+    &pb_dec_fixed32,
+    &pb_dec_fixed64,
+    
+    &pb_dec_bytes,
+    &pb_dec_string,
+    &pb_dec_submessage,
+    NULL, /* extensions */
+    &pb_dec_bytes /* PB_LTYPE_FIXED_LENGTH_BYTES */
+};
+
+/*******************************
+ * pb_istream_t implementation *
+ *******************************/
+
+static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count)
+{
+    const pb_byte_t *source = (const pb_byte_t*)stream->state;
+    stream->state = (pb_byte_t*)stream->state + count;
+    
+    if (buf != NULL)
+    {
+        while (count--)
+            *buf++ = *source++;
+    }
+    
+    return true;
+}
+
+bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count)
+{
+#ifndef PB_BUFFER_ONLY
+	if (buf == NULL && stream->callback != buf_read)
+	{
+		/* Skip input bytes */
+		pb_byte_t tmp[16];
+		while (count > 16)
+		{
+			if (!pb_read(stream, tmp, 16))
+				return false;
+			
+			count -= 16;
+		}
+		
+		return pb_read(stream, tmp, count);
+	}
+#endif
+
+    if (stream->bytes_left < count)
+        PB_RETURN_ERROR(stream, "end-of-stream");
+    
+#ifndef PB_BUFFER_ONLY
+    if (!stream->callback(stream, buf, count))
+        PB_RETURN_ERROR(stream, "io error");
+#else
+    if (!buf_read(stream, buf, count))
+        return false;
+#endif
+    
+    stream->bytes_left -= count;
+    return true;
+}
+
+/* Read a single byte from input stream. buf may not be NULL.
+ * This is an optimization for the varint decoding. */
+static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf)
+{
+    if (stream->bytes_left == 0)
+        PB_RETURN_ERROR(stream, "end-of-stream");
+
+#ifndef PB_BUFFER_ONLY
+    if (!stream->callback(stream, buf, 1))
+        PB_RETURN_ERROR(stream, "io error");
+#else
+    *buf = *(const pb_byte_t*)stream->state;
+    stream->state = (pb_byte_t*)stream->state + 1;
+#endif
+
+    stream->bytes_left--;
+    
+    return true;    
+}
+
+pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize)
+{
+    pb_istream_t stream;
+    /* Cast away the const from buf without a compiler error.  We are
+     * careful to use it only in a const manner in the callbacks.
+     */
+    union {
+        void *state;
+        const void *c_state;
+    } state;
+#ifdef PB_BUFFER_ONLY
+    stream.callback = NULL;
+#else
+    stream.callback = &buf_read;
+#endif
+    state.c_state = buf;
+    stream.state = state.state;
+    stream.bytes_left = bufsize;
+#ifndef PB_NO_ERRMSG
+    stream.errmsg = NULL;
+#endif
+    return stream;
+}
+
+/********************
+ * Helper functions *
+ ********************/
+
+static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
+{
+    pb_byte_t byte;
+    uint32_t result;
+    
+    if (!pb_readbyte(stream, &byte))
+        return false;
+    
+    if ((byte & 0x80) == 0)
+    {
+        /* Quick case, 1 byte value */
+        result = byte;
+    }
+    else
+    {
+        /* Multibyte case */
+        uint_fast8_t bitpos = 7;
+        result = byte & 0x7F;
+        
+        do
+        {
+            if (bitpos >= 32)
+                PB_RETURN_ERROR(stream, "varint overflow");
+            
+            if (!pb_readbyte(stream, &byte))
+                return false;
+            
+            result |= (uint32_t)(byte & 0x7F) << bitpos;
+            bitpos = (uint_fast8_t)(bitpos + 7);
+        } while (byte & 0x80);
+   }
+   
+   *dest = result;
+   return true;
+}
+
+bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest)
+{
+    pb_byte_t byte;
+    uint_fast8_t bitpos = 0;
+    uint64_t result = 0;
+    
+    do
+    {
+        if (bitpos >= 64)
+            PB_RETURN_ERROR(stream, "varint overflow");
+        
+        if (!pb_readbyte(stream, &byte))
+            return false;
+
+        result |= (uint64_t)(byte & 0x7F) << bitpos;
+        bitpos = (uint_fast8_t)(bitpos + 7);
+    } while (byte & 0x80);
+    
+    *dest = result;
+    return true;
+}
+
+bool checkreturn pb_skip_varint(pb_istream_t *stream)
+{
+    pb_byte_t byte;
+    do
+    {
+        if (!pb_read(stream, &byte, 1))
+            return false;
+    } while (byte & 0x80);
+    return true;
+}
+
+bool checkreturn pb_skip_string(pb_istream_t *stream)
+{
+    uint32_t length;
+    if (!pb_decode_varint32(stream, &length))
+        return false;
+    
+    return pb_read(stream, NULL, length);
+}
+
+bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof)
+{
+    uint32_t temp;
+    *eof = false;
+    *wire_type = (pb_wire_type_t) 0;
+    *tag = 0;
+    
+    if (!pb_decode_varint32(stream, &temp))
+    {
+        if (stream->bytes_left == 0)
+            *eof = true;
+
+        return false;
+    }
+    
+    if (temp == 0)
+    {
+        *eof = true; /* Special feature: allow 0-terminated messages. */
+        return false;
+    }
+    
+    *tag = temp >> 3;
+    *wire_type = (pb_wire_type_t)(temp & 7);
+    return true;
+}
+
+bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type)
+{
+    switch (wire_type)
+    {
+        case PB_WT_VARINT: return pb_skip_varint(stream);
+        case PB_WT_64BIT: return pb_read(stream, NULL, 8);
+        case PB_WT_STRING: return pb_skip_string(stream);
+        case PB_WT_32BIT: return pb_read(stream, NULL, 4);
+        default: PB_RETURN_ERROR(stream, "invalid wire_type");
+    }
+}
+
+/* Read a raw value to buffer, for the purpose of passing it to callback as
+ * a substream. Size is maximum size on call, and actual size on return.
+ */
+static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size)
+{
+    size_t max_size = *size;
+    switch (wire_type)
+    {
+        case PB_WT_VARINT:
+            *size = 0;
+            do
+            {
+                (*size)++;
+                if (*size > max_size) return false;
+                if (!pb_read(stream, buf, 1)) return false;
+            } while (*buf++ & 0x80);
+            return true;
+            
+        case PB_WT_64BIT:
+            *size = 8;
+            return pb_read(stream, buf, 8);
+        
+        case PB_WT_32BIT:
+            *size = 4;
+            return pb_read(stream, buf, 4);
+        
+        default: PB_RETURN_ERROR(stream, "invalid wire_type");
+    }
+}
+
+/* Decode string length from stream and return a substream with limited length.
+ * Remember to close the substream using pb_close_string_substream().
+ */
+bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream)
+{
+    uint32_t size;
+    if (!pb_decode_varint32(stream, &size))
+        return false;
+    
+    *substream = *stream;
+    if (substream->bytes_left < size)
+        PB_RETURN_ERROR(stream, "parent stream too short");
+    
+    substream->bytes_left = size;
+    stream->bytes_left -= size;
+    return true;
+}
+
+void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream)
+{
+    stream->state = substream->state;
+
+#ifndef PB_NO_ERRMSG
+    stream->errmsg = substream->errmsg;
+#endif
+}
+
+/*************************
+ * Decode a single field *
+ *************************/
+
+static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+    pb_type_t type;
+    pb_decoder_t func;
+    
+    type = iter->pos->type;
+    func = PB_DECODERS[PB_LTYPE(type)];
+
+    switch (PB_HTYPE(type))
+    {
+        case PB_HTYPE_REQUIRED:
+            return func(stream, iter->pos, iter->pData);
+            
+        case PB_HTYPE_OPTIONAL:
+            *(bool*)iter->pSize = true;
+            return func(stream, iter->pos, iter->pData);
+    
+        case PB_HTYPE_REPEATED:
+            if (wire_type == PB_WT_STRING
+                && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
+            {
+                /* Packed array */
+                bool status = true;
+                pb_size_t *size = (pb_size_t*)iter->pSize;
+                pb_istream_t substream;
+                if (!pb_make_string_substream(stream, &substream))
+                    return false;
+                
+                while (substream.bytes_left > 0 && *size < iter->pos->array_size)
+                {
+                    void *pItem = (char*)iter->pData + iter->pos->data_size * (*size);
+                    if (!func(&substream, iter->pos, pItem))
+                    {
+                        status = false;
+                        break;
+                    }
+                    (*size)++;
+                }
+                pb_close_string_substream(stream, &substream);
+                
+                if (substream.bytes_left != 0)
+                    PB_RETURN_ERROR(stream, "array overflow");
+                
+                return status;
+            }
+            else
+            {
+                /* Repeated field */
+                pb_size_t *size = (pb_size_t*)iter->pSize;
+                void *pItem = (char*)iter->pData + iter->pos->data_size * (*size);
+                if (*size >= iter->pos->array_size)
+                    PB_RETURN_ERROR(stream, "array overflow");
+                
+                (*size)++;
+                return func(stream, iter->pos, pItem);
+            }
+
+        case PB_HTYPE_ONEOF:
+            *(pb_size_t*)iter->pSize = iter->pos->tag;
+            if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE)
+            {
+                /* We memset to zero so that any callbacks are set to NULL.
+                 * Then set any default values. */
+                memset(iter->pData, 0, iter->pos->data_size);
+                pb_message_set_to_defaults((const pb_field_t*)iter->pos->ptr, iter->pData);
+            }
+            return func(stream, iter->pos, iter->pData);
+
+        default:
+            PB_RETURN_ERROR(stream, "invalid field type");
+    }
+}
+
+#ifdef PB_ENABLE_MALLOC
+/* Allocate storage for the field and store the pointer at iter->pData.
+ * array_size is the number of entries to reserve in an array.
+ * Zero size is not allowed, use pb_free() for releasing.
+ */
+static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size)
+{    
+    void *ptr = *(void**)pData;
+    
+    if (data_size == 0 || array_size == 0)
+        PB_RETURN_ERROR(stream, "invalid size");
+    
+    /* Check for multiplication overflows.
+     * This code avoids the costly division if the sizes are small enough.
+     * Multiplication is safe as long as only half of bits are set
+     * in either multiplicand.
+     */
+    {
+        const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4);
+        if (data_size >= check_limit || array_size >= check_limit)
+        {
+            const size_t size_max = (size_t)-1;
+            if (size_max / array_size < data_size)
+            {
+                PB_RETURN_ERROR(stream, "size too large");
+            }
+        }
+    }
+    
+    /* Allocate new or expand previous allocation */
+    /* Note: on failure the old pointer will remain in the structure,
+     * the message must be freed by caller also on error return. */
+    ptr = pb_realloc(ptr, array_size * data_size);
+    if (ptr == NULL)
+        PB_RETURN_ERROR(stream, "realloc failed");
+    
+    *(void**)pData = ptr;
+    return true;
+}
+
+/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */
+static void initialize_pointer_field(void *pItem, pb_field_iter_t *iter)
+{
+    if (PB_LTYPE(iter->pos->type) == PB_LTYPE_STRING ||
+        PB_LTYPE(iter->pos->type) == PB_LTYPE_BYTES)
+    {
+        *(void**)pItem = NULL;
+    }
+    else if (PB_LTYPE(iter->pos->type) == PB_LTYPE_SUBMESSAGE)
+    {
+        pb_message_set_to_defaults((const pb_field_t *) iter->pos->ptr, pItem);
+    }
+}
+#endif
+
+static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+#ifndef PB_ENABLE_MALLOC
+    PB_UNUSED(wire_type);
+    PB_UNUSED(iter);
+    PB_RETURN_ERROR(stream, "no malloc support");
+#else
+    pb_type_t type;
+    pb_decoder_t func;
+    
+    type = iter->pos->type;
+    func = PB_DECODERS[PB_LTYPE(type)];
+    
+    switch (PB_HTYPE(type))
+    {
+        case PB_HTYPE_REQUIRED:
+        case PB_HTYPE_OPTIONAL:
+        case PB_HTYPE_ONEOF:
+            if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE &&
+                *(void**)iter->pData != NULL)
+            {
+                /* Duplicate field, have to release the old allocation first. */
+                pb_release_single_field(iter);
+            }
+        
+            if (PB_HTYPE(type) == PB_HTYPE_ONEOF)
+            {
+                *(pb_size_t*)iter->pSize = iter->pos->tag;
+            }
+
+            if (PB_LTYPE(type) == PB_LTYPE_STRING ||
+                PB_LTYPE(type) == PB_LTYPE_BYTES)
+            {
+                return func(stream, iter->pos, iter->pData);
+            }
+            else
+            {
+                if (!allocate_field(stream, iter->pData, iter->pos->data_size, 1))
+                    return false;
+                
+                initialize_pointer_field(*(void**)iter->pData, iter);
+                return func(stream, iter->pos, *(void**)iter->pData);
+            }
+    
+        case PB_HTYPE_REPEATED:
+            if (wire_type == PB_WT_STRING
+                && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
+            {
+                /* Packed array, multiple items come in at once. */
+                bool status = true;
+                pb_size_t *size = (pb_size_t*)iter->pSize;
+                size_t allocated_size = *size;
+                void *pItem;
+                pb_istream_t substream;
+                
+                if (!pb_make_string_substream(stream, &substream))
+                    return false;
+                
+                while (substream.bytes_left)
+                {
+                    if ((size_t)*size + 1 > allocated_size)
+                    {
+                        /* Allocate more storage. This tries to guess the
+                         * number of remaining entries. Round the division
+                         * upwards. */
+                        allocated_size += (substream.bytes_left - 1) / iter->pos->data_size + 1;
+                        
+                        if (!allocate_field(&substream, iter->pData, iter->pos->data_size, allocated_size))
+                        {
+                            status = false;
+                            break;
+                        }
+                    }
+
+                    /* Decode the array entry */
+                    pItem = *(char**)iter->pData + iter->pos->data_size * (*size);
+                    initialize_pointer_field(pItem, iter);
+                    if (!func(&substream, iter->pos, pItem))
+                    {
+                        status = false;
+                        break;
+                    }
+                    
+                    if (*size == PB_SIZE_MAX)
+                    {
+#ifndef PB_NO_ERRMSG
+                        stream->errmsg = "too many array entries";
+#endif
+                        status = false;
+                        break;
+                    }
+                    
+                    (*size)++;
+                }
+                pb_close_string_substream(stream, &substream);
+                
+                return status;
+            }
+            else
+            {
+                /* Normal repeated field, i.e. only one item at a time. */
+                pb_size_t *size = (pb_size_t*)iter->pSize;
+                void *pItem;
+                
+                if (*size == PB_SIZE_MAX)
+                    PB_RETURN_ERROR(stream, "too many array entries");
+                
+                (*size)++;
+                if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size))
+                    return false;
+            
+                pItem = *(char**)iter->pData + iter->pos->data_size * (*size - 1);
+                initialize_pointer_field(pItem, iter);
+                return func(stream, iter->pos, pItem);
+            }
+
+        default:
+            PB_RETURN_ERROR(stream, "invalid field type");
+    }
+#endif
+}
+
+static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+    pb_callback_t *pCallback = (pb_callback_t*)iter->pData;
+    
+#ifdef PB_OLD_CALLBACK_STYLE
+    void *arg = pCallback->arg;
+#else
+    void **arg = &(pCallback->arg);
+#endif
+    
+    if (pCallback->funcs.decode == NULL)
+        return pb_skip_field(stream, wire_type);
+    
+    if (wire_type == PB_WT_STRING)
+    {
+        pb_istream_t substream;
+        
+        if (!pb_make_string_substream(stream, &substream))
+            return false;
+        
+        do
+        {
+            if (!pCallback->funcs.decode(&substream, iter->pos, arg))
+                PB_RETURN_ERROR(stream, "callback failed");
+        } while (substream.bytes_left);
+        
+        pb_close_string_substream(stream, &substream);
+        return true;
+    }
+    else
+    {
+        /* Copy the single scalar value to stack.
+         * This is required so that we can limit the stream length,
+         * which in turn allows to use same callback for packed and
+         * not-packed fields. */
+        pb_istream_t substream;
+        pb_byte_t buffer[10];
+        size_t size = sizeof(buffer);
+        
+        if (!read_raw_value(stream, wire_type, buffer, &size))
+            return false;
+        substream = pb_istream_from_buffer(buffer, size);
+        
+        return pCallback->funcs.decode(&substream, iter->pos, arg);
+    }
+}
+
+static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+#ifdef PB_ENABLE_MALLOC
+    /* When decoding an oneof field, check if there is old data that must be
+     * released first. */
+    if (PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF)
+    {
+        if (!pb_release_union_field(stream, iter))
+            return false;
+    }
+#endif
+
+    switch (PB_ATYPE(iter->pos->type))
+    {
+        case PB_ATYPE_STATIC:
+            return decode_static_field(stream, wire_type, iter);
+        
+        case PB_ATYPE_POINTER:
+            return decode_pointer_field(stream, wire_type, iter);
+        
+        case PB_ATYPE_CALLBACK:
+            return decode_callback_field(stream, wire_type, iter);
+        
+        default:
+            PB_RETURN_ERROR(stream, "invalid field type");
+    }
+}
+
+static void iter_from_extension(pb_field_iter_t *iter, pb_extension_t *extension)
+{
+    /* Fake a field iterator for the extension field.
+     * It is not actually safe to advance this iterator, but decode_field
+     * will not even try to. */
+    const pb_field_t *field = (const pb_field_t*)extension->type->arg;
+    (void)pb_field_iter_begin(iter, field, extension->dest);
+    iter->pData = extension->dest;
+    iter->pSize = &extension->found;
+    
+    if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+    {
+        /* For pointer extensions, the pointer is stored directly
+         * in the extension structure. This avoids having an extra
+         * indirection. */
+        iter->pData = &extension->dest;
+    }
+}
+
+/* Default handler for extension fields. Expects a pb_field_t structure
+ * in extension->type->arg. */
+static bool checkreturn default_extension_decoder(pb_istream_t *stream,
+    pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type)
+{
+    const pb_field_t *field = (const pb_field_t*)extension->type->arg;
+    pb_field_iter_t iter;
+    
+    if (field->tag != tag)
+        return true;
+    
+    iter_from_extension(&iter, extension);
+    extension->found = true;
+    return decode_field(stream, wire_type, &iter);
+}
+
+/* Try to decode an unknown field as an extension field. Tries each extension
+ * decoder in turn, until one of them handles the field or loop ends. */
+static bool checkreturn decode_extension(pb_istream_t *stream,
+    uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+    pb_extension_t *extension = *(pb_extension_t* const *)iter->pData;
+    size_t pos = stream->bytes_left;
+    
+    while (extension != NULL && pos == stream->bytes_left)
+    {
+        bool status;
+        if (extension->type->decode)
+            status = extension->type->decode(stream, extension, tag, wire_type);
+        else
+            status = default_extension_decoder(stream, extension, tag, wire_type);
+
+        if (!status)
+            return false;
+        
+        extension = extension->next;
+    }
+    
+    return true;
+}
+
+/* Step through the iterator until an extension field is found or until all
+ * entries have been checked. There can be only one extension field per
+ * message. Returns false if no extension field is found. */
+static bool checkreturn find_extension_field(pb_field_iter_t *iter)
+{
+    const pb_field_t *start = iter->pos;
+    
+    do {
+        if (PB_LTYPE(iter->pos->type) == PB_LTYPE_EXTENSION)
+            return true;
+        (void)pb_field_iter_next(iter);
+    } while (iter->pos != start);
+    
+    return false;
+}
+
+/* Initialize message fields to default values, recursively */
+static void pb_field_set_to_default(pb_field_iter_t *iter)
+{
+    pb_type_t type;
+    type = iter->pos->type;
+    
+    if (PB_LTYPE(type) == PB_LTYPE_EXTENSION)
+    {
+        pb_extension_t *ext = *(pb_extension_t* const *)iter->pData;
+        while (ext != NULL)
+        {
+            pb_field_iter_t ext_iter;
+            ext->found = false;
+            iter_from_extension(&ext_iter, ext);
+            pb_field_set_to_default(&ext_iter);
+            ext = ext->next;
+        }
+    }
+    else if (PB_ATYPE(type) == PB_ATYPE_STATIC)
+    {
+        bool init_data = true;
+        if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL)
+        {
+            /* Set has_field to false. Still initialize the optional field
+             * itself also. */
+            *(bool*)iter->pSize = false;
+        }
+        else if (PB_HTYPE(type) == PB_HTYPE_REPEATED ||
+                 PB_HTYPE(type) == PB_HTYPE_ONEOF)
+        {
+            /* REPEATED: Set array count to 0, no need to initialize contents.
+               ONEOF: Set which_field to 0. */
+            *(pb_size_t*)iter->pSize = 0;
+            init_data = false;
+        }
+
+        if (init_data)
+        {
+            if (PB_LTYPE(iter->pos->type) == PB_LTYPE_SUBMESSAGE)
+            {
+                /* Initialize submessage to defaults */
+                pb_message_set_to_defaults((const pb_field_t *) iter->pos->ptr, iter->pData);
+            }
+            else if (iter->pos->ptr != NULL)
+            {
+                /* Initialize to default value */
+                memcpy(iter->pData, iter->pos->ptr, iter->pos->data_size);
+            }
+            else
+            {
+                /* Initialize to zeros */
+                memset(iter->pData, 0, iter->pos->data_size);
+            }
+        }
+    }
+    else if (PB_ATYPE(type) == PB_ATYPE_POINTER)
+    {
+        /* Initialize the pointer to NULL. */
+        *(void**)iter->pData = NULL;
+        
+        /* Initialize array count to 0. */
+        if (PB_HTYPE(type) == PB_HTYPE_REPEATED ||
+            PB_HTYPE(type) == PB_HTYPE_ONEOF)
+        {
+            *(pb_size_t*)iter->pSize = 0;
+        }
+    }
+    else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK)
+    {
+        /* Don't overwrite callback */
+    }
+}
+
+static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct)
+{
+    pb_field_iter_t iter;
+
+    if (!pb_field_iter_begin(&iter, fields, dest_struct))
+        return; /* Empty message type */
+    
+    do
+    {
+        pb_field_set_to_default(&iter);
+    } while (pb_field_iter_next(&iter));
+}
+
+/*********************
+ * Decode all fields *
+ *********************/
+
+bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+    uint32_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 31) / 32] = {0, 0};
+    const uint32_t allbits = ~(uint32_t)0;
+    uint32_t extension_range_start = 0;
+    pb_field_iter_t iter;
+    
+    /* Return value ignored, as empty message types will be correctly handled by
+     * pb_field_iter_find() anyway. */
+    (void)pb_field_iter_begin(&iter, fields, dest_struct);
+    
+    while (stream->bytes_left)
+    {
+        uint32_t tag;
+        pb_wire_type_t wire_type;
+        bool eof;
+        
+        if (!pb_decode_tag(stream, &wire_type, &tag, &eof))
+        {
+            if (eof)
+                break;
+            else
+                return false;
+        }
+        
+        if (!pb_field_iter_find(&iter, tag))
+        {
+            /* No match found, check if it matches an extension. */
+            if (tag >= extension_range_start)
+            {
+                if (!find_extension_field(&iter))
+                    extension_range_start = (uint32_t)-1;
+                else
+                    extension_range_start = iter.pos->tag;
+                
+                if (tag >= extension_range_start)
+                {
+                    size_t pos = stream->bytes_left;
+                
+                    if (!decode_extension(stream, tag, wire_type, &iter))
+                        return false;
+                    
+                    if (pos != stream->bytes_left)
+                    {
+                        /* The field was handled */
+                        continue;                    
+                    }
+                }
+            }
+        
+            /* No match found, skip data */
+            if (!pb_skip_field(stream, wire_type))
+                return false;
+            continue;
+        }
+        
+        if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REQUIRED
+            && iter.required_field_index < PB_MAX_REQUIRED_FIELDS)
+        {
+            uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31));
+            fields_seen[iter.required_field_index >> 5] |= tmp;
+        }
+            
+        if (!decode_field(stream, wire_type, &iter))
+            return false;
+    }
+    
+    /* Check that all required fields were present. */
+    {
+        /* First figure out the number of required fields by
+         * seeking to the end of the field array. Usually we
+         * are already close to end after decoding.
+         */
+        unsigned req_field_count;
+        pb_type_t last_type;
+        unsigned i;
+        do {
+            req_field_count = iter.required_field_index;
+            last_type = iter.pos->type;
+        } while (pb_field_iter_next(&iter));
+        
+        /* Fixup if last field was also required. */
+        if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED && iter.pos->tag != 0)
+            req_field_count++;
+        
+        if (req_field_count > 0)
+        {
+            /* Check the whole words */
+            for (i = 0; i < (req_field_count >> 5); i++)
+            {
+                if (fields_seen[i] != allbits)
+                    PB_RETURN_ERROR(stream, "missing required field");
+            }
+            
+            /* Check the remaining bits */
+            if (fields_seen[req_field_count >> 5] != (allbits >> (32 - (req_field_count & 31))))
+                PB_RETURN_ERROR(stream, "missing required field");
+        }
+    }
+    
+    return true;
+}
+
+bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+    bool status;
+    pb_message_set_to_defaults(fields, dest_struct);
+    status = pb_decode_noinit(stream, fields, dest_struct);
+    
+#ifdef PB_ENABLE_MALLOC
+    if (!status)
+        pb_release(fields, dest_struct);
+#endif
+    
+    return status;
+}
+
+bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+    pb_istream_t substream;
+    bool status;
+    
+    if (!pb_make_string_substream(stream, &substream))
+        return false;
+    
+    status = pb_decode(&substream, fields, dest_struct);
+    pb_close_string_substream(stream, &substream);
+    return status;
+}
+
+#ifdef PB_ENABLE_MALLOC
+/* Given an oneof field, if there has already been a field inside this oneof,
+ * release it before overwriting with a different one. */
+static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter)
+{
+    pb_size_t old_tag = *(pb_size_t*)iter->pSize; /* Previous which_ value */
+    pb_size_t new_tag = iter->pos->tag; /* New which_ value */
+
+    if (old_tag == 0)
+        return true; /* Ok, no old data in union */
+
+    if (old_tag == new_tag)
+        return true; /* Ok, old data is of same type => merge */
+
+    /* Release old data. The find can fail if the message struct contains
+     * invalid data. */
+    if (!pb_field_iter_find(iter, old_tag))
+        PB_RETURN_ERROR(stream, "invalid union tag");
+
+    pb_release_single_field(iter);
+
+    /* Restore iterator to where it should be.
+     * This shouldn't fail unless the pb_field_t structure is corrupted. */
+    if (!pb_field_iter_find(iter, new_tag))
+        PB_RETURN_ERROR(stream, "iterator error");
+    
+    return true;
+}
+
+static void pb_release_single_field(const pb_field_iter_t *iter)
+{
+    pb_type_t type;
+    type = iter->pos->type;
+
+    if (PB_HTYPE(type) == PB_HTYPE_ONEOF)
+    {
+        if (*(pb_size_t*)iter->pSize != iter->pos->tag)
+            return; /* This is not the current field in the union */
+    }
+
+    /* Release anything contained inside an extension or submsg.
+     * This has to be done even if the submsg itself is statically
+     * allocated. */
+    if (PB_LTYPE(type) == PB_LTYPE_EXTENSION)
+    {
+        /* Release fields from all extensions in the linked list */
+        pb_extension_t *ext = *(pb_extension_t**)iter->pData;
+        while (ext != NULL)
+        {
+            pb_field_iter_t ext_iter;
+            iter_from_extension(&ext_iter, ext);
+            pb_release_single_field(&ext_iter);
+            ext = ext->next;
+        }
+    }
+    else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE)
+    {
+        /* Release fields in submessage or submsg array */
+        void *pItem = iter->pData;
+        pb_size_t count = 1;
+        
+        if (PB_ATYPE(type) == PB_ATYPE_POINTER)
+        {
+            pItem = *(void**)iter->pData;
+        }
+        
+        if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
+        {
+            count = *(pb_size_t*)iter->pSize;
+
+            if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > iter->pos->array_size)
+            {
+                /* Protect against corrupted _count fields */
+                count = iter->pos->array_size;
+            }
+        }
+        
+        if (pItem)
+        {
+            while (count--)
+            {
+                pb_release((const pb_field_t*)iter->pos->ptr, pItem);
+                pItem = (char*)pItem + iter->pos->data_size;
+            }
+        }
+    }
+    
+    if (PB_ATYPE(type) == PB_ATYPE_POINTER)
+    {
+        if (PB_HTYPE(type) == PB_HTYPE_REPEATED &&
+            (PB_LTYPE(type) == PB_LTYPE_STRING ||
+             PB_LTYPE(type) == PB_LTYPE_BYTES))
+        {
+            /* Release entries in repeated string or bytes array */
+            void **pItem = *(void***)iter->pData;
+            pb_size_t count = *(pb_size_t*)iter->pSize;
+            while (count--)
+            {
+                pb_free(*pItem);
+                *pItem++ = NULL;
+            }
+        }
+        
+        if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
+        {
+            /* We are going to release the array, so set the size to 0 */
+            *(pb_size_t*)iter->pSize = 0;
+        }
+        
+        /* Release main item */
+        pb_free(*(void**)iter->pData);
+        *(void**)iter->pData = NULL;
+    }
+}
+
+void pb_release(const pb_field_t fields[], void *dest_struct)
+{
+    pb_field_iter_t iter;
+    
+    if (!dest_struct)
+        return; /* Ignore NULL pointers, similar to free() */
+
+    if (!pb_field_iter_begin(&iter, fields, dest_struct))
+        return; /* Empty message type */
+    
+    do
+    {
+        pb_release_single_field(&iter);
+    } while (pb_field_iter_next(&iter));
+}
+#endif
+
+/* Field decoders */
+
+bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest)
+{
+    uint64_t value;
+    if (!pb_decode_varint(stream, &value))
+        return false;
+    
+    if (value & 1)
+        *dest = (int64_t)(~(value >> 1));
+    else
+        *dest = (int64_t)(value >> 1);
+    
+    return true;
+}
+
+bool pb_decode_fixed32(pb_istream_t *stream, void *dest)
+{
+    pb_byte_t bytes[4];
+
+    if (!pb_read(stream, bytes, 4))
+        return false;
+    
+    *(uint32_t*)dest = ((uint32_t)bytes[0] << 0) |
+                       ((uint32_t)bytes[1] << 8) |
+                       ((uint32_t)bytes[2] << 16) |
+                       ((uint32_t)bytes[3] << 24);
+    return true;
+}
+
+bool pb_decode_fixed64(pb_istream_t *stream, void *dest)
+{
+    pb_byte_t bytes[8];
+
+    if (!pb_read(stream, bytes, 8))
+        return false;
+    
+    *(uint64_t*)dest = ((uint64_t)bytes[0] << 0) |
+                       ((uint64_t)bytes[1] << 8) |
+                       ((uint64_t)bytes[2] << 16) |
+                       ((uint64_t)bytes[3] << 24) |
+                       ((uint64_t)bytes[4] << 32) |
+                       ((uint64_t)bytes[5] << 40) |
+                       ((uint64_t)bytes[6] << 48) |
+                       ((uint64_t)bytes[7] << 56);
+    
+    return true;
+}
+
+static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    uint64_t value;
+    int64_t svalue;
+    int64_t clamped;
+    if (!pb_decode_varint(stream, &value))
+        return false;
+    
+    /* See issue 97: Google's C++ protobuf allows negative varint values to
+     * be cast as int32_t, instead of the int64_t that should be used when
+     * encoding. Previous nanopb versions had a bug in encoding. In order to
+     * not break decoding of such messages, we cast <=32 bit fields to
+     * int32_t first to get the sign correct.
+     */
+    if (field->data_size == sizeof(int64_t))
+        svalue = (int64_t)value;
+    else
+        svalue = (int32_t)value;
+
+    /* Cast to the proper field size, while checking for overflows */
+    if (field->data_size == sizeof(int64_t))
+        clamped = *(int64_t*)dest = svalue;
+    else if (field->data_size == sizeof(int32_t))
+        clamped = *(int32_t*)dest = (int32_t)svalue;
+    else if (field->data_size == sizeof(int_least16_t))
+        clamped = *(int_least16_t*)dest = (int_least16_t)svalue;
+    else if (field->data_size == sizeof(int_least8_t))
+        clamped = *(int_least8_t*)dest = (int_least8_t)svalue;
+    else
+        PB_RETURN_ERROR(stream, "invalid data_size");
+
+    if (clamped != svalue)
+        PB_RETURN_ERROR(stream, "integer too large");
+    
+    return true;
+}
+
+static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    uint64_t value, clamped;
+    if (!pb_decode_varint(stream, &value))
+        return false;
+    
+    /* Cast to the proper field size, while checking for overflows */
+    if (field->data_size == sizeof(uint64_t))
+        clamped = *(uint64_t*)dest = value;
+    else if (field->data_size == sizeof(uint32_t))
+        clamped = *(uint32_t*)dest = (uint32_t)value;
+    else if (field->data_size == sizeof(uint_least16_t))
+        clamped = *(uint_least16_t*)dest = (uint_least16_t)value;
+    else if (field->data_size == sizeof(uint_least8_t))
+        clamped = *(uint_least8_t*)dest = (uint_least8_t)value;
+    else
+        PB_RETURN_ERROR(stream, "invalid data_size");
+    
+    if (clamped != value)
+        PB_RETURN_ERROR(stream, "integer too large");
+
+    return true;
+}
+
+static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    int64_t value, clamped;
+    if (!pb_decode_svarint(stream, &value))
+        return false;
+    
+    /* Cast to the proper field size, while checking for overflows */
+    if (field->data_size == sizeof(int64_t))
+        clamped = *(int64_t*)dest = value;
+    else if (field->data_size == sizeof(int32_t))
+        clamped = *(int32_t*)dest = (int32_t)value;
+    else if (field->data_size == sizeof(int_least16_t))
+        clamped = *(int_least16_t*)dest = (int_least16_t)value;
+    else if (field->data_size == sizeof(int_least8_t))
+        clamped = *(int_least8_t*)dest = (int_least8_t)value;
+    else
+        PB_RETURN_ERROR(stream, "invalid data_size");
+
+    if (clamped != value)
+        PB_RETURN_ERROR(stream, "integer too large");
+    
+    return true;
+}
+
+static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    PB_UNUSED(field);
+    return pb_decode_fixed32(stream, dest);
+}
+
+static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    PB_UNUSED(field);
+    return pb_decode_fixed64(stream, dest);
+}
+
+static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    uint32_t size;
+    size_t alloc_size;
+    pb_bytes_array_t *bdest;
+    
+    if (!pb_decode_varint32(stream, &size))
+        return false;
+    
+    if (size > PB_SIZE_MAX)
+        PB_RETURN_ERROR(stream, "bytes overflow");
+    
+    alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size);
+    if (size > alloc_size)
+        PB_RETURN_ERROR(stream, "size too large");
+    
+    if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+    {
+#ifndef PB_ENABLE_MALLOC
+        PB_RETURN_ERROR(stream, "no malloc support");
+#else
+        if (!allocate_field(stream, dest, alloc_size, 1))
+            return false;
+        bdest = *(pb_bytes_array_t**)dest;
+#endif
+    }
+    else
+    {
+        if (PB_LTYPE(field->type) == PB_LTYPE_FIXED_LENGTH_BYTES) {
+            if (size != field->data_size)
+                PB_RETURN_ERROR(stream, "incorrect inline bytes size");
+            return pb_read(stream, (pb_byte_t*)dest, field->data_size);
+        }
+
+        if (alloc_size > field->data_size)
+            PB_RETURN_ERROR(stream, "bytes overflow");
+        bdest = (pb_bytes_array_t*)dest;
+    }
+
+    bdest->size = (pb_size_t)size;
+    return pb_read(stream, bdest->bytes, size);
+}
+
+static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    uint32_t size;
+    size_t alloc_size;
+    bool status;
+    if (!pb_decode_varint32(stream, &size))
+        return false;
+    
+    /* Space for null terminator */
+    alloc_size = size + 1;
+    
+    if (alloc_size < size)
+        PB_RETURN_ERROR(stream, "size too large");
+    
+    if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+    {
+#ifndef PB_ENABLE_MALLOC
+        PB_RETURN_ERROR(stream, "no malloc support");
+#else
+        if (!allocate_field(stream, dest, alloc_size, 1))
+            return false;
+        dest = *(void**)dest;
+#endif
+    }
+    else
+    {
+        if (alloc_size > field->data_size)
+            PB_RETURN_ERROR(stream, "string overflow");
+    }
+    
+    status = pb_read(stream, (pb_byte_t*)dest, size);
+    *((pb_byte_t*)dest + size) = 0;
+    return status;
+}
+
+static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    bool status;
+    pb_istream_t substream;
+    const pb_field_t* submsg_fields = (const pb_field_t*)field->ptr;
+    
+    if (!pb_make_string_substream(stream, &substream))
+        return false;
+    
+    if (field->ptr == NULL)
+        PB_RETURN_ERROR(stream, "invalid field descriptor");
+    
+    /* New array entries need to be initialized, while required and optional
+     * submessages have already been initialized in the top-level pb_decode. */
+    if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
+        status = pb_decode(&substream, submsg_fields, dest);
+    else
+        status = pb_decode_noinit(&substream, submsg_fields, dest);
+    
+    pb_close_string_substream(stream, &substream);
+    return status;
+}
diff --git a/third_party/nanopb/pb_decode.h b/third_party/nanopb/pb_decode.h
new file mode 100644
index 0000000..1d9bb19
--- /dev/null
+++ b/third_party/nanopb/pb_decode.h
@@ -0,0 +1,149 @@
+/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c.
+ * The main function is pb_decode. You also need an input stream, and the
+ * field descriptions created by nanopb_generator.py.
+ */
+
+#ifndef PB_DECODE_H_INCLUDED
+#define PB_DECODE_H_INCLUDED
+
+#include "pb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structure for defining custom input streams. You will need to provide
+ * a callback function to read the bytes from your storage, which can be
+ * for example a file or a network socket.
+ * 
+ * The callback must conform to these rules:
+ *
+ * 1) Return false on IO errors. This will cause decoding to abort.
+ * 2) You can use state to store your own data (e.g. buffer pointer),
+ *    and rely on pb_read to verify that no-body reads past bytes_left.
+ * 3) Your callback may be used with substreams, in which case bytes_left
+ *    is different than from the main stream. Don't use bytes_left to compute
+ *    any pointers.
+ */
+struct pb_istream_s
+{
+#ifdef PB_BUFFER_ONLY
+    /* Callback pointer is not used in buffer-only configuration.
+     * Having an int pointer here allows binary compatibility but
+     * gives an error if someone tries to assign callback function.
+     */
+    int *callback;
+#else
+    bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count);
+#endif
+
+    void *state; /* Free field for use by callback implementation */
+    size_t bytes_left;
+    
+#ifndef PB_NO_ERRMSG
+    const char *errmsg;
+#endif
+};
+
+/***************************
+ * Main decoding functions *
+ ***************************/
+ 
+/* Decode a single protocol buffers message from input stream into a C structure.
+ * Returns true on success, false on any failure.
+ * The actual struct pointed to by dest must match the description in fields.
+ * Callback fields of the destination structure must be initialized by caller.
+ * All other fields will be initialized by this function.
+ *
+ * Example usage:
+ *    MyMessage msg = {};
+ *    uint8_t buffer[64];
+ *    pb_istream_t stream;
+ *    
+ *    // ... read some data into buffer ...
+ *
+ *    stream = pb_istream_from_buffer(buffer, count);
+ *    pb_decode(&stream, MyMessage_fields, &msg);
+ */
+bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
+
+/* Same as pb_decode, except does not initialize the destination structure
+ * to default values. This is slightly faster if you need no default values
+ * and just do memset(struct, 0, sizeof(struct)) yourself.
+ *
+ * This can also be used for 'merging' two messages, i.e. update only the
+ * fields that exist in the new message.
+ *
+ * Note: If this function returns with an error, it will not release any
+ * dynamically allocated fields. You will need to call pb_release() yourself.
+ */
+bool pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
+
+/* Same as pb_decode, except expects the stream to start with the message size
+ * encoded as varint. Corresponds to parseDelimitedFrom() in Google's
+ * protobuf API.
+ */
+bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
+
+#ifdef PB_ENABLE_MALLOC
+/* Release any allocated pointer fields. If you use dynamic allocation, you should
+ * call this for any successfully decoded message when you are done with it. If
+ * pb_decode() returns with an error, the message is already released.
+ */
+void pb_release(const pb_field_t fields[], void *dest_struct);
+#endif
+
+
+/**************************************
+ * Functions for manipulating streams *
+ **************************************/
+
+/* Create an input stream for reading from a memory buffer.
+ *
+ * Alternatively, you can use a custom stream that reads directly from e.g.
+ * a file or a network socket.
+ */
+pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize);
+
+/* Function to read from a pb_istream_t. You can use this if you need to
+ * read some custom header data, or to read data in field callbacks.
+ */
+bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);
+
+
+/************************************************
+ * Helper functions for writing field callbacks *
+ ************************************************/
+
+/* Decode the tag for the next field in the stream. Gives the wire type and
+ * field tag. At end of the message, returns false and sets eof to true. */
+bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof);
+
+/* Skip the field payload data, given the wire type. */
+bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
+
+/* Decode an integer in the varint format. This works for bool, enum, int32,
+ * int64, uint32 and uint64 field types. */
+bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
+
+/* Decode an integer in the zig-zagged svarint format. This works for sint32
+ * and sint64. */
+bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
+
+/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to
+ * a 4-byte wide C variable. */
+bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
+
+/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to
+ * a 8-byte wide C variable. */
+bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
+
+/* Make a limited-length substream for reading a PB_WT_STRING field. */
+bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
+void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/third_party/nanopb/pb_encode.c b/third_party/nanopb/pb_encode.c
new file mode 100644
index 0000000..4685614
--- /dev/null
+++ b/third_party/nanopb/pb_encode.c
@@ -0,0 +1,696 @@
+/* pb_encode.c -- encode a protobuf using minimal resources
+ *
+ * 2011 Petteri Aimonen <jpa@kapsi.fi>
+ */
+
+#include "pb.h"
+#include "pb_encode.h"
+#include "pb_common.h"
+
+/* Use the GCC warn_unused_result attribute to check that all return values
+ * are propagated correctly. On other compilers and gcc before 3.4.0 just
+ * ignore the annotation.
+ */
+#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
+    #define checkreturn
+#else
+    #define checkreturn __attribute__((warn_unused_result))
+#endif
+
+/**************************************
+ * Declarations internal to this file *
+ **************************************/
+typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn;
+
+static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
+static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, const void *pData, size_t count, pb_encoder_t func);
+static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
+static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension);
+static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
+static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+
+/* --- Function pointers to field encoders ---
+ * Order in the array must match pb_action_t LTYPE numbering.
+ */
+static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = {
+    &pb_enc_varint,
+    &pb_enc_uvarint,
+    &pb_enc_svarint,
+    &pb_enc_fixed32,
+    &pb_enc_fixed64,
+    
+    &pb_enc_bytes,
+    &pb_enc_string,
+    &pb_enc_submessage,
+    NULL, /* extensions */
+    &pb_enc_bytes /* PB_LTYPE_FIXED_LENGTH_BYTES */
+};
+
+/*******************************
+ * pb_ostream_t implementation *
+ *******************************/
+
+static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
+{
+    pb_byte_t *dest = (pb_byte_t*)stream->state;
+    stream->state = dest + count;
+    
+    while (count--)
+        *dest++ = *buf++;
+    
+    return true;
+}
+
+pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize)
+{
+    pb_ostream_t stream;
+#ifdef PB_BUFFER_ONLY
+    stream.callback = (void*)1; /* Just a marker value */
+#else
+    stream.callback = &buf_write;
+#endif
+    stream.state = buf;
+    stream.max_size = bufsize;
+    stream.bytes_written = 0;
+#ifndef PB_NO_ERRMSG
+    stream.errmsg = NULL;
+#endif
+    return stream;
+}
+
+bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
+{
+    if (stream->callback != NULL)
+    {
+        if (stream->bytes_written + count > stream->max_size)
+            PB_RETURN_ERROR(stream, "stream full");
+
+#ifdef PB_BUFFER_ONLY
+        if (!buf_write(stream, buf, count))
+            PB_RETURN_ERROR(stream, "io error");
+#else        
+        if (!stream->callback(stream, buf, count))
+            PB_RETURN_ERROR(stream, "io error");
+#endif
+    }
+    
+    stream->bytes_written += count;
+    return true;
+}
+
+/*************************
+ * Encode a single field *
+ *************************/
+
+/* Encode a static array. Handles the size calculations and possible packing. */
+static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
+                         const void *pData, size_t count, pb_encoder_t func)
+{
+    size_t i;
+    const void *p;
+    size_t size;
+    
+    if (count == 0)
+        return true;
+
+    if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size)
+        PB_RETURN_ERROR(stream, "array max size exceeded");
+    
+    /* We always pack arrays if the datatype allows it. */
+    if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
+    {
+        if (!pb_encode_tag(stream, PB_WT_STRING, field->tag))
+            return false;
+        
+        /* Determine the total size of packed array. */
+        if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32)
+        {
+            size = 4 * count;
+        }
+        else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
+        {
+            size = 8 * count;
+        }
+        else
+        { 
+            pb_ostream_t sizestream = PB_OSTREAM_SIZING;
+            p = pData;
+            for (i = 0; i < count; i++)
+            {
+                if (!func(&sizestream, field, p))
+                    return false;
+                p = (const char*)p + field->data_size;
+            }
+            size = sizestream.bytes_written;
+        }
+        
+        if (!pb_encode_varint(stream, (uint64_t)size))
+            return false;
+        
+        if (stream->callback == NULL)
+            return pb_write(stream, NULL, size); /* Just sizing.. */
+        
+        /* Write the data */
+        p = pData;
+        for (i = 0; i < count; i++)
+        {
+            if (!func(stream, field, p))
+                return false;
+            p = (const char*)p + field->data_size;
+        }
+    }
+    else
+    {
+        p = pData;
+        for (i = 0; i < count; i++)
+        {
+            if (!pb_encode_tag_for_field(stream, field))
+                return false;
+
+            /* Normally the data is stored directly in the array entries, but
+             * for pointer-type string and bytes fields, the array entries are
+             * actually pointers themselves also. So we have to dereference once
+             * more to get to the actual data. */
+            if (PB_ATYPE(field->type) == PB_ATYPE_POINTER &&
+                (PB_LTYPE(field->type) == PB_LTYPE_STRING ||
+                 PB_LTYPE(field->type) == PB_LTYPE_BYTES))
+            {
+                if (!func(stream, field, *(const void* const*)p))
+                    return false;      
+            }
+            else
+            {
+                if (!func(stream, field, p))
+                    return false;
+            }
+            p = (const char*)p + field->data_size;
+        }
+    }
+    
+    return true;
+}
+
+/* Encode a field with static or pointer allocation, i.e. one whose data
+ * is available to the encoder directly. */
+static bool checkreturn encode_basic_field(pb_ostream_t *stream,
+    const pb_field_t *field, const void *pData)
+{
+    pb_encoder_t func;
+    const void *pSize;
+    bool implicit_has = true;
+    
+    func = PB_ENCODERS[PB_LTYPE(field->type)];
+    
+    if (field->size_offset)
+        pSize = (const char*)pData + field->size_offset;
+    else
+        pSize = &implicit_has;
+
+    if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+    {
+        /* pData is a pointer to the field, which contains pointer to
+         * the data. If the 2nd pointer is NULL, it is interpreted as if
+         * the has_field was false.
+         */
+        
+        pData = *(const void* const*)pData;
+        implicit_has = (pData != NULL);
+    }
+
+    switch (PB_HTYPE(field->type))
+    {
+        case PB_HTYPE_REQUIRED:
+            if (!pData)
+                PB_RETURN_ERROR(stream, "missing required field");
+            if (!pb_encode_tag_for_field(stream, field))
+                return false;
+            if (!func(stream, field, pData))
+                return false;
+            break;
+        
+        case PB_HTYPE_OPTIONAL:
+            if (*(const bool*)pSize)
+            {
+                if (!pb_encode_tag_for_field(stream, field))
+                    return false;
+            
+                if (!func(stream, field, pData))
+                    return false;
+            }
+            break;
+        
+        case PB_HTYPE_REPEATED:
+            if (!encode_array(stream, field, pData, *(const pb_size_t*)pSize, func))
+                return false;
+            break;
+        
+        case PB_HTYPE_ONEOF:
+            if (*(const pb_size_t*)pSize == field->tag)
+            {
+                if (!pb_encode_tag_for_field(stream, field))
+                    return false;
+
+                if (!func(stream, field, pData))
+                    return false;
+            }
+            break;
+            
+        default:
+            PB_RETURN_ERROR(stream, "invalid field type");
+    }
+    
+    return true;
+}
+
+/* Encode a field with callback semantics. This means that a user function is
+ * called to provide and encode the actual data. */
+static bool checkreturn encode_callback_field(pb_ostream_t *stream,
+    const pb_field_t *field, const void *pData)
+{
+    const pb_callback_t *callback = (const pb_callback_t*)pData;
+    
+#ifdef PB_OLD_CALLBACK_STYLE
+    const void *arg = callback->arg;
+#else
+    void * const *arg = &(callback->arg);
+#endif    
+    
+    if (callback->funcs.encode != NULL)
+    {
+        if (!callback->funcs.encode(stream, field, arg))
+            PB_RETURN_ERROR(stream, "callback error");
+    }
+    return true;
+}
+
+/* Encode a single field of any callback or static type. */
+static bool checkreturn encode_field(pb_ostream_t *stream,
+    const pb_field_t *field, const void *pData)
+{
+    switch (PB_ATYPE(field->type))
+    {
+        case PB_ATYPE_STATIC:
+        case PB_ATYPE_POINTER:
+            return encode_basic_field(stream, field, pData);
+        
+        case PB_ATYPE_CALLBACK:
+            return encode_callback_field(stream, field, pData);
+        
+        default:
+            PB_RETURN_ERROR(stream, "invalid field type");
+    }
+}
+
+/* Default handler for extension fields. Expects to have a pb_field_t
+ * pointer in the extension->type->arg field. */
+static bool checkreturn default_extension_encoder(pb_ostream_t *stream,
+    const pb_extension_t *extension)
+{
+    const pb_field_t *field = (const pb_field_t*)extension->type->arg;
+    
+    if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+    {
+        /* For pointer extensions, the pointer is stored directly
+         * in the extension structure. This avoids having an extra
+         * indirection. */
+        return encode_field(stream, field, &extension->dest);
+    }
+    else
+    {
+        return encode_field(stream, field, extension->dest);
+    }
+}
+
+/* Walk through all the registered extensions and give them a chance
+ * to encode themselves. */
+static bool checkreturn encode_extension_field(pb_ostream_t *stream,
+    const pb_field_t *field, const void *pData)
+{
+    const pb_extension_t *extension = *(const pb_extension_t* const *)pData;
+    PB_UNUSED(field);
+    
+    while (extension)
+    {
+        bool status;
+        if (extension->type->encode)
+            status = extension->type->encode(stream, extension);
+        else
+            status = default_extension_encoder(stream, extension);
+
+        if (!status)
+            return false;
+        
+        extension = extension->next;
+    }
+    
+    return true;
+}
+
+/*********************
+ * Encode all fields *
+ *********************/
+
+static void *remove_const(const void *p)
+{
+    /* Note: this casts away const, in order to use the common field iterator
+     * logic for both encoding and decoding. */
+    union {
+        void *p1;
+        const void *p2;
+    } t;
+    t.p2 = p;
+    return t.p1;
+}
+
+bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
+{
+    pb_field_iter_t iter;
+    if (!pb_field_iter_begin(&iter, fields, remove_const(src_struct)))
+        return true; /* Empty message type */
+    
+    do {
+        if (PB_LTYPE(iter.pos->type) == PB_LTYPE_EXTENSION)
+        {
+            /* Special case for the extension field placeholder */
+            if (!encode_extension_field(stream, iter.pos, iter.pData))
+                return false;
+        }
+        else
+        {
+            /* Regular field */
+            if (!encode_field(stream, iter.pos, iter.pData))
+                return false;
+        }
+    } while (pb_field_iter_next(&iter));
+    
+    return true;
+}
+
+bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
+{
+    return pb_encode_submessage(stream, fields, src_struct);
+}
+
+bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct)
+{
+    pb_ostream_t stream = PB_OSTREAM_SIZING;
+    
+    if (!pb_encode(&stream, fields, src_struct))
+        return false;
+    
+    *size = stream.bytes_written;
+    return true;
+}
+
+/********************
+ * Helper functions *
+ ********************/
+bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
+{
+    pb_byte_t buffer[10];
+    size_t i = 0;
+    
+    if (value <= 0x7F)
+    {
+        pb_byte_t v = (pb_byte_t)value;
+        return pb_write(stream, &v, 1);
+    }
+    
+    while (value)
+    {
+        buffer[i] = (pb_byte_t)((value & 0x7F) | 0x80);
+        value >>= 7;
+        i++;
+    }
+    buffer[i-1] &= 0x7F; /* Unset top bit on last byte */
+    
+    return pb_write(stream, buffer, i);
+}
+
+bool checkreturn pb_encode_svarint(pb_ostream_t *stream, int64_t value)
+{
+    uint64_t zigzagged;
+    if (value < 0)
+        zigzagged = ~((uint64_t)value << 1);
+    else
+        zigzagged = (uint64_t)value << 1;
+    
+    return pb_encode_varint(stream, zigzagged);
+}
+
+bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value)
+{
+    uint32_t val = *(const uint32_t*)value;
+    pb_byte_t bytes[4];
+    bytes[0] = (pb_byte_t)(val & 0xFF);
+    bytes[1] = (pb_byte_t)((val >> 8) & 0xFF);
+    bytes[2] = (pb_byte_t)((val >> 16) & 0xFF);
+    bytes[3] = (pb_byte_t)((val >> 24) & 0xFF);
+    return pb_write(stream, bytes, 4);
+}
+
+bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
+{
+    uint64_t val = *(const uint64_t*)value;
+    pb_byte_t bytes[8];
+    bytes[0] = (pb_byte_t)(val & 0xFF);
+    bytes[1] = (pb_byte_t)((val >> 8) & 0xFF);
+    bytes[2] = (pb_byte_t)((val >> 16) & 0xFF);
+    bytes[3] = (pb_byte_t)((val >> 24) & 0xFF);
+    bytes[4] = (pb_byte_t)((val >> 32) & 0xFF);
+    bytes[5] = (pb_byte_t)((val >> 40) & 0xFF);
+    bytes[6] = (pb_byte_t)((val >> 48) & 0xFF);
+    bytes[7] = (pb_byte_t)((val >> 56) & 0xFF);
+    return pb_write(stream, bytes, 8);
+}
+
+bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number)
+{
+    uint64_t tag = ((uint64_t)field_number << 3) | wiretype;
+    return pb_encode_varint(stream, tag);
+}
+
+bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field)
+{
+    pb_wire_type_t wiretype;
+    switch (PB_LTYPE(field->type))
+    {
+        case PB_LTYPE_VARINT:
+        case PB_LTYPE_UVARINT:
+        case PB_LTYPE_SVARINT:
+            wiretype = PB_WT_VARINT;
+            break;
+        
+        case PB_LTYPE_FIXED32:
+            wiretype = PB_WT_32BIT;
+            break;
+        
+        case PB_LTYPE_FIXED64:
+            wiretype = PB_WT_64BIT;
+            break;
+        
+        case PB_LTYPE_BYTES:
+        case PB_LTYPE_STRING:
+        case PB_LTYPE_SUBMESSAGE:
+        case PB_LTYPE_FIXED_LENGTH_BYTES:
+            wiretype = PB_WT_STRING;
+            break;
+        
+        default:
+            PB_RETURN_ERROR(stream, "invalid field type");
+    }
+    
+    return pb_encode_tag(stream, wiretype, field->tag);
+}
+
+bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size)
+{
+    if (!pb_encode_varint(stream, (uint64_t)size))
+        return false;
+    
+    return pb_write(stream, buffer, size);
+}
+
+bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
+{
+    /* First calculate the message size using a non-writing substream. */
+    pb_ostream_t substream = PB_OSTREAM_SIZING;
+    size_t size;
+    bool status;
+    
+    if (!pb_encode(&substream, fields, src_struct))
+    {
+#ifndef PB_NO_ERRMSG
+        stream->errmsg = substream.errmsg;
+#endif
+        return false;
+    }
+    
+    size = substream.bytes_written;
+    
+    if (!pb_encode_varint(stream, (uint64_t)size))
+        return false;
+    
+    if (stream->callback == NULL)
+        return pb_write(stream, NULL, size); /* Just sizing */
+    
+    if (stream->bytes_written + size > stream->max_size)
+        PB_RETURN_ERROR(stream, "stream full");
+        
+    /* Use a substream to verify that a callback doesn't write more than
+     * what it did the first time. */
+    substream.callback = stream->callback;
+    substream.state = stream->state;
+    substream.max_size = size;
+    substream.bytes_written = 0;
+#ifndef PB_NO_ERRMSG
+    substream.errmsg = NULL;
+#endif
+    
+    status = pb_encode(&substream, fields, src_struct);
+    
+    stream->bytes_written += substream.bytes_written;
+    stream->state = substream.state;
+#ifndef PB_NO_ERRMSG
+    stream->errmsg = substream.errmsg;
+#endif
+    
+    if (substream.bytes_written != size)
+        PB_RETURN_ERROR(stream, "submsg size changed");
+    
+    return status;
+}
+
+/* Field encoders */
+
+static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    int64_t value = 0;
+    
+    if (field->data_size == sizeof(int_least8_t))
+        value = *(const int_least8_t*)src;
+    else if (field->data_size == sizeof(int_least16_t))
+        value = *(const int_least16_t*)src;
+    else if (field->data_size == sizeof(int32_t))
+        value = *(const int32_t*)src;
+    else if (field->data_size == sizeof(int64_t))
+        value = *(const int64_t*)src;
+    else
+        PB_RETURN_ERROR(stream, "invalid data_size");
+    
+    return pb_encode_varint(stream, (uint64_t)value);
+}
+
+static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    uint64_t value = 0;
+    
+    if (field->data_size == sizeof(uint_least8_t))
+        value = *(const uint_least8_t*)src;
+    else if (field->data_size == sizeof(uint_least16_t))
+        value = *(const uint_least16_t*)src;
+    else if (field->data_size == sizeof(uint32_t))
+        value = *(const uint32_t*)src;
+    else if (field->data_size == sizeof(uint64_t))
+        value = *(const uint64_t*)src;
+    else
+        PB_RETURN_ERROR(stream, "invalid data_size");
+    
+    return pb_encode_varint(stream, value);
+}
+
+static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    int64_t value = 0;
+    
+    if (field->data_size == sizeof(int_least8_t))
+        value = *(const int_least8_t*)src;
+    else if (field->data_size == sizeof(int_least16_t))
+        value = *(const int_least16_t*)src;
+    else if (field->data_size == sizeof(int32_t))
+        value = *(const int32_t*)src;
+    else if (field->data_size == sizeof(int64_t))
+        value = *(const int64_t*)src;
+    else
+        PB_RETURN_ERROR(stream, "invalid data_size");
+    
+    return pb_encode_svarint(stream, value);
+}
+
+static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    PB_UNUSED(field);
+    return pb_encode_fixed64(stream, src);
+}
+
+static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    PB_UNUSED(field);
+    return pb_encode_fixed32(stream, src);
+}
+
+static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    const pb_bytes_array_t *bytes = NULL;
+
+    if (PB_LTYPE(field->type) == PB_LTYPE_FIXED_LENGTH_BYTES)
+        return pb_encode_string(stream, (const pb_byte_t*)src, field->data_size);
+
+    bytes = (const pb_bytes_array_t*)src;
+    
+    if (src == NULL)
+    {
+        /* Treat null pointer as an empty bytes field */
+        return pb_encode_string(stream, NULL, 0);
+    }
+    
+    if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
+        PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) > field->data_size)
+    {
+        PB_RETURN_ERROR(stream, "bytes size exceeded");
+    }
+    
+    return pb_encode_string(stream, bytes->bytes, bytes->size);
+}
+
+static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    size_t size = 0;
+    size_t max_size = field->data_size;
+    const char *p = (const char*)src;
+    
+    if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+        max_size = (size_t)-1;
+
+    if (src == NULL)
+    {
+        size = 0; /* Treat null pointer as an empty string */
+    }
+    else
+    {
+        /* strnlen() is not always available, so just use a loop */
+        while (size < max_size && *p != '\0')
+        {
+            size++;
+            p++;
+        }
+    }
+
+    return pb_encode_string(stream, (const pb_byte_t*)src, size);
+}
+
+static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    if (field->ptr == NULL)
+        PB_RETURN_ERROR(stream, "invalid field descriptor");
+    
+    return pb_encode_submessage(stream, (const pb_field_t*)field->ptr, src);
+}
+
diff --git a/third_party/nanopb/pb_encode.h b/third_party/nanopb/pb_encode.h
new file mode 100644
index 0000000..d9909fb
--- /dev/null
+++ b/third_party/nanopb/pb_encode.h
@@ -0,0 +1,154 @@
+/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c.
+ * The main function is pb_encode. You also need an output stream, and the
+ * field descriptions created by nanopb_generator.py.
+ */
+
+#ifndef PB_ENCODE_H_INCLUDED
+#define PB_ENCODE_H_INCLUDED
+
+#include "pb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structure for defining custom output streams. You will need to provide
+ * a callback function to write the bytes to your storage, which can be
+ * for example a file or a network socket.
+ *
+ * The callback must conform to these rules:
+ *
+ * 1) Return false on IO errors. This will cause encoding to abort.
+ * 2) You can use state to store your own data (e.g. buffer pointer).
+ * 3) pb_write will update bytes_written after your callback runs.
+ * 4) Substreams will modify max_size and bytes_written. Don't use them
+ *    to calculate any pointers.
+ */
+struct pb_ostream_s
+{
+#ifdef PB_BUFFER_ONLY
+    /* Callback pointer is not used in buffer-only configuration.
+     * Having an int pointer here allows binary compatibility but
+     * gives an error if someone tries to assign callback function.
+     * Also, NULL pointer marks a 'sizing stream' that does not
+     * write anything.
+     */
+    int *callback;
+#else
+    bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
+#endif
+    void *state;          /* Free field for use by callback implementation. */
+    size_t max_size;      /* Limit number of output bytes written (or use SIZE_MAX). */
+    size_t bytes_written; /* Number of bytes written so far. */
+    
+#ifndef PB_NO_ERRMSG
+    const char *errmsg;
+#endif
+};
+
+/***************************
+ * Main encoding functions *
+ ***************************/
+
+/* Encode a single protocol buffers message from C structure into a stream.
+ * Returns true on success, false on any failure.
+ * The actual struct pointed to by src_struct must match the description in fields.
+ * All required fields in the struct are assumed to have been filled in.
+ *
+ * Example usage:
+ *    MyMessage msg = {};
+ *    uint8_t buffer[64];
+ *    pb_ostream_t stream;
+ *
+ *    msg.field1 = 42;
+ *    stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+ *    pb_encode(&stream, MyMessage_fields, &msg);
+ */
+bool pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
+
+/* Same as pb_encode, but prepends the length of the message as a varint.
+ * Corresponds to writeDelimitedTo() in Google's protobuf API.
+ */
+bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
+
+/* Encode the message to get the size of the encoded data, but do not store
+ * the data. */
+bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct);
+
+/**************************************
+ * Functions for manipulating streams *
+ **************************************/
+
+/* Create an output stream for writing into a memory buffer.
+ * The number of bytes written can be found in stream.bytes_written after
+ * encoding the message.
+ *
+ * Alternatively, you can use a custom stream that writes directly to e.g.
+ * a file or a network socket.
+ */
+pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize);
+
+/* Pseudo-stream for measuring the size of a message without actually storing
+ * the encoded data.
+ * 
+ * Example usage:
+ *    MyMessage msg = {};
+ *    pb_ostream_t stream = PB_OSTREAM_SIZING;
+ *    pb_encode(&stream, MyMessage_fields, &msg);
+ *    printf("Message size is %d\n", stream.bytes_written);
+ */
+#ifndef PB_NO_ERRMSG
+#define PB_OSTREAM_SIZING {0,0,0,0,0}
+#else
+#define PB_OSTREAM_SIZING {0,0,0,0}
+#endif
+
+/* Function to write into a pb_ostream_t stream. You can use this if you need
+ * to append or prepend some custom headers to the message.
+ */
+bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
+
+
+/************************************************
+ * Helper functions for writing field callbacks *
+ ************************************************/
+
+/* Encode field header based on type and field number defined in the field
+ * structure. Call this from the callback before writing out field contents. */
+bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field);
+
+/* Encode field header by manually specifing wire type. You need to use this
+ * if you want to write out packed arrays from a callback field. */
+bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number);
+
+/* Encode an integer in the varint format.
+ * This works for bool, enum, int32, int64, uint32 and uint64 field types. */
+bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
+
+/* Encode an integer in the zig-zagged svarint format.
+ * This works for sint32 and sint64. */
+bool pb_encode_svarint(pb_ostream_t *stream, int64_t value);
+
+/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */
+bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size);
+
+/* Encode a fixed32, sfixed32 or float value.
+ * You need to pass a pointer to a 4-byte wide C variable. */
+bool pb_encode_fixed32(pb_ostream_t *stream, const void *value);
+
+/* Encode a fixed64, sfixed64 or double value.
+ * You need to pass a pointer to a 8-byte wide C variable. */
+bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
+
+/* Encode a submessage field.
+ * You need to pass the pb_field_t array and pointer to struct, just like
+ * with pb_encode(). This internally encodes the submessage twice, first to
+ * calculate message size and then to actually write it out.
+ */
+bool pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/third_party/nanopb/tests/Makefile b/third_party/nanopb/tests/Makefile
new file mode 100644
index 0000000..cee6bf6
--- /dev/null
+++ b/third_party/nanopb/tests/Makefile
@@ -0,0 +1,21 @@
+all:
+	scons
+
+clean:
+	scons -c
+
+coverage:
+	rm -rf build coverage
+
+	# LCOV does not like the newer gcov format
+	scons CC=gcc-4.6 CXX=gcc-4.6
+
+	# Collect the data
+	mkdir build/coverage
+	lcov --base-directory . --directory build/ --gcov-tool gcov-4.6 -c -o build/coverage/nanopb.info
+
+	# Remove the test code from results
+	lcov -r build/coverage/nanopb.info '*tests*' -o build/coverage/nanopb.info
+
+	# Generate HTML
+	genhtml -o build/coverage build/coverage/nanopb.info
diff --git a/third_party/nanopb/tests/SConstruct b/third_party/nanopb/tests/SConstruct
new file mode 100644
index 0000000..d8ab9ab
--- /dev/null
+++ b/third_party/nanopb/tests/SConstruct
@@ -0,0 +1,155 @@
+Help('''
+Type 'scons' to build and run all the available test cases.
+It will automatically detect your platform and C compiler and
+build appropriately.
+
+You can modify the behavious using following options:
+CC          Name of C compiler
+CXX         Name of C++ compiler
+CCFLAGS     Flags to pass to the C compiler
+CXXFLAGS    Flags to pass to the C++ compiler
+
+For example, for a clang build, use:
+scons CC=clang CXX=clang++
+''')
+
+import os
+env = Environment(ENV = os.environ, tools = ['default', 'nanopb'])
+
+# Allow overriding the compiler with scons CC=???
+if 'CC' in ARGUMENTS: env.Replace(CC = ARGUMENTS['CC'])
+if 'CXX' in ARGUMENTS: env.Replace(CXX = ARGUMENTS['CXX'])
+if 'CCFLAGS' in ARGUMENTS: env.Append(CCFLAGS = ARGUMENTS['CCFLAGS'])
+if 'CXXFLAGS' in ARGUMENTS: env.Append(CXXFLAGS = ARGUMENTS['CXXFLAGS'])
+
+# Add the builders defined in site_init.py
+add_nanopb_builders(env)
+
+# Path to the files shared by tests, and to the nanopb core.
+env.Append(CPPPATH = ["#../", "$COMMON"])
+
+# Path for finding nanopb.proto
+env.Append(PROTOCPATH = '#../generator')
+
+# Check the compilation environment, unless we are just cleaning up.
+if not env.GetOption('clean'):
+    def check_ccflags(context, flags, linkflags = ''):
+        '''Check if given CCFLAGS are supported'''
+        context.Message('Checking support for CCFLAGS="%s"... ' % flags)
+        oldflags = context.env['CCFLAGS']
+        oldlinkflags = context.env['CCFLAGS']
+        context.env.Append(CCFLAGS = flags)
+        context.env.Append(LINKFLAGS = linkflags)
+        result = context.TryCompile("int main() {return 0;}", '.c')
+        context.env.Replace(CCFLAGS = oldflags)
+        context.env.Replace(LINKFLAGS = oldlinkflags)
+        context.Result(result)
+        return result
+    
+    conf = Configure(env, custom_tests = {'CheckCCFLAGS': check_ccflags})
+
+    # If the platform doesn't support C99, use our own header file instead.
+    stdbool = conf.CheckCHeader('stdbool.h')
+    stdint = conf.CheckCHeader('stdint.h')
+    stddef = conf.CheckCHeader('stddef.h')
+    string = conf.CheckCHeader('string.h')
+    stdlib = conf.CheckCHeader('stdlib.h')
+    if not stdbool or not stdint or not stddef or not string:
+        conf.env.Append(CPPDEFINES = {'PB_SYSTEM_HEADER': '\\"pb_syshdr.h\\"'})
+        conf.env.Append(CPPPATH = "#../extra")
+	conf.env.Append(SYSHDR = '\\"pb_syshdr.h\\"')
+        
+        if stdbool: conf.env.Append(CPPDEFINES = {'HAVE_STDBOOL_H': 1})
+        if stdint: conf.env.Append(CPPDEFINES = {'HAVE_STDINT_H': 1})
+        if stddef: conf.env.Append(CPPDEFINES = {'HAVE_STDDEF_H': 1})
+        if string: conf.env.Append(CPPDEFINES = {'HAVE_STRING_H': 1})
+        if stdlib: conf.env.Append(CPPDEFINES = {'HAVE_STDLIB_H': 1})
+    
+    # Check if we can use pkg-config to find protobuf include path
+    status, output = conf.TryAction('pkg-config protobuf --variable=includedir > $TARGET')
+    if status:
+        conf.env.Append(PROTOCPATH = output.strip())
+    else:
+        conf.env.Append(PROTOCPATH = '/usr/include')
+    
+    # Check protoc version
+    status, output = conf.TryAction('$PROTOC --version > $TARGET')
+    if status:
+        conf.env['PROTOC_VERSION'] = output
+
+    # Check if libmudflap is available (only with GCC)
+    if 'gcc' in env['CC']:
+        if conf.CheckLib('mudflap'):
+            conf.env.Append(CCFLAGS = '-fmudflap')
+            conf.env.Append(LINKFLAGS = '-fmudflap')
+    
+    # Check if we can use extra strict warning flags (only with GCC)
+    extra = '-Wcast-qual -Wlogical-op -Wconversion'
+    extra += ' -fstrict-aliasing -Wstrict-aliasing=1'
+    extra += ' -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls'
+    extra += ' -Wstack-protector '
+    if 'gcc' in env['CC']:
+        if conf.CheckCCFLAGS(extra):
+            conf.env.Append(CORECFLAGS = extra)
+    
+    # Check if we can use undefined behaviour sanitizer (only with clang)
+    extra = '-fsanitize=undefined '
+    if 'clang' in env['CC']:
+        if conf.CheckCCFLAGS(extra, linkflags = extra):
+            conf.env.Append(CORECFLAGS = extra)
+            conf.env.Append(LINKFLAGS = extra)
+    
+    # End the config stuff
+    env = conf.Finish()
+
+# Initialize the CCFLAGS according to the compiler
+if 'gcc' in env['CC']:
+    # GNU Compiler Collection
+    
+    # Debug info, warnings as errors
+    env.Append(CFLAGS = '-ansi -pedantic -g -Wall -Werror -fprofile-arcs -ftest-coverage ')
+    env.Append(CORECFLAGS = '-Wextra')
+    env.Append(LINKFLAGS = '-g --coverage')
+    
+    # We currently need uint64_t anyway, even though ANSI C90 otherwise..
+    env.Append(CFLAGS = '-Wno-long-long')
+elif 'clang' in env['CC']:
+    # CLang
+    env.Append(CFLAGS = '-ansi -g -Wall -Werror')
+    env.Append(CORECFLAGS = ' -Wextra -Wcast-qual -Wconversion')
+elif 'cl' in env['CC']:
+    # Microsoft Visual C++
+    
+    # Debug info on, warning level 2 for tests, warnings as errors
+    env.Append(CFLAGS = '/Zi /W2 /WX')
+    env.Append(LINKFLAGS = '/DEBUG')
+    
+    # More strict checks on the nanopb core
+    env.Append(CORECFLAGS = '/W4')
+elif 'tcc' in env['CC']:
+    # Tiny C Compiler
+    env.Append(CFLAGS = '-Wall -Werror -g')
+
+env.SetDefault(CORECFLAGS = '')
+
+if 'clang' in env['CXX']:
+    env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers')
+elif 'g++' in env['CXX'] or 'gcc' in env['CXX']:
+    env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers')
+elif 'cl' in env['CXX']:
+    env.Append(CXXFLAGS = '/Zi /W2 /WX')
+
+# Now include the SConscript files from all subdirectories
+import os.path
+env['VARIANT_DIR'] = 'build'
+env['BUILD'] = '#' + env['VARIANT_DIR']
+env['COMMON'] = '#' + env['VARIANT_DIR'] + '/common'
+
+# Include common/SConscript first to make sure its exports are available
+# to other SConscripts.
+SConscript("common/SConscript", exports = 'env', variant_dir = env['VARIANT_DIR'] + '/common')
+
+for subdir in Glob('*/SConscript') + Glob('regression/*/SConscript'):
+    if str(subdir).startswith("common"): continue
+    SConscript(subdir, exports = 'env', variant_dir = env['VARIANT_DIR'] + '/' + os.path.dirname(str(subdir)))
+
diff --git a/third_party/nanopb/tests/alltypes/SConscript b/third_party/nanopb/tests/alltypes/SConscript
new file mode 100644
index 0000000..6c6238c
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes/SConscript
@@ -0,0 +1,35 @@
+# Build and run a test that encodes and decodes a message that contains
+# all of the Protocol Buffers data types.
+
+Import("env")
+
+env.NanopbProto(["alltypes", "alltypes.options"])
+enc = env.Program(["encode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"])
+dec = env.Program(["decode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"])
+
+# Test the round-trip from nanopb encoder to nanopb decoder
+env.RunTest(enc)
+env.RunTest([dec, "encode_alltypes.output"])
+
+# Re-encode the data using protoc, and check that the results from nanopb
+# match byte-per-byte to the protoc output.
+env.Decode("encode_alltypes.output.decoded",
+           ["encode_alltypes.output", "alltypes.proto"],
+           MESSAGE='AllTypes')
+env.Encode("encode_alltypes.output.recoded",
+           ["encode_alltypes.output.decoded", "alltypes.proto"],
+           MESSAGE='AllTypes')
+env.Compare(["encode_alltypes.output", "encode_alltypes.output.recoded"])
+
+# Do the same checks with the optional fields present.
+env.RunTest("optionals.output", enc, ARGS = ['1'])
+env.RunTest("optionals.decout", [dec, "optionals.output"], ARGS = ['1'])
+env.Decode("optionals.output.decoded",
+           ["optionals.output", "alltypes.proto"],
+           MESSAGE='AllTypes')
+env.Encode("optionals.output.recoded",
+           ["optionals.output.decoded", "alltypes.proto"],
+           MESSAGE='AllTypes')
+env.Compare(["optionals.output", "optionals.output.recoded"])
+
+
diff --git a/third_party/nanopb/tests/alltypes/alltypes.options b/third_party/nanopb/tests/alltypes/alltypes.options
new file mode 100644
index 0000000..b31e3cf
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes/alltypes.options
@@ -0,0 +1,3 @@
+* max_size:16
+* max_count:5
+
diff --git a/third_party/nanopb/tests/alltypes/alltypes.proto b/third_party/nanopb/tests/alltypes/alltypes.proto
new file mode 100644
index 0000000..3995c55
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes/alltypes.proto
@@ -0,0 +1,123 @@
+syntax = "proto2";
+// package name placeholder
+
+message SubMessage {
+    required string substuff1 = 1 [default = "1"];
+    required int32 substuff2 = 2 [default = 2];
+    optional fixed32 substuff3 = 3 [default = 3];
+}
+
+message EmptyMessage {
+
+}
+
+enum HugeEnum {
+    Negative = -2147483647; /* protoc doesn't accept -2147483648 here */
+    Positive =  2147483647;
+}
+
+message Limits {
+    required int32      int32_min  =  1 [default = 2147483647];
+    required int32      int32_max  =  2 [default = -2147483647];
+    required uint32     uint32_min =  3 [default = 4294967295];
+    required uint32     uint32_max =  4 [default = 0];
+    required int64      int64_min  =  5 [default = 9223372036854775807];
+    required int64      int64_max  =  6 [default = -9223372036854775807];
+    required uint64     uint64_min =  7 [default = 18446744073709551615];
+    required uint64     uint64_max =  8 [default = 0];
+    required HugeEnum   enum_min   =  9 [default = Positive];
+    required HugeEnum   enum_max   = 10 [default = Negative];
+}
+
+enum MyEnum {
+    Zero = 0;
+    First = 1;
+    Second = 2;
+    Truth = 42;
+}
+
+message AllTypes {
+    required int32      req_int32   = 1;
+    required int64      req_int64   = 2;
+    required uint32     req_uint32  = 3;
+    required uint64     req_uint64  = 4;
+    required sint32     req_sint32  = 5;
+    required sint64     req_sint64  = 6;
+    required bool       req_bool    = 7;
+    
+    required fixed32    req_fixed32 = 8;
+    required sfixed32   req_sfixed32= 9;
+    required float      req_float   = 10;
+    
+    required fixed64    req_fixed64 = 11;
+    required sfixed64   req_sfixed64= 12;
+    required double     req_double  = 13;
+    
+    required string     req_string  = 14;
+    required bytes      req_bytes   = 15;
+    required SubMessage req_submsg  = 16;
+    required MyEnum     req_enum    = 17;
+    required EmptyMessage req_emptymsg = 18;
+    
+    
+    repeated int32      rep_int32   = 21 [packed = true];
+    repeated int64      rep_int64   = 22 [packed = true];
+    repeated uint32     rep_uint32  = 23 [packed = true];
+    repeated uint64     rep_uint64  = 24 [packed = true];
+    repeated sint32     rep_sint32  = 25 [packed = true];
+    repeated sint64     rep_sint64  = 26 [packed = true];
+    repeated bool       rep_bool    = 27 [packed = true];
+    
+    repeated fixed32    rep_fixed32 = 28 [packed = true];
+    repeated sfixed32   rep_sfixed32= 29 [packed = true];
+    repeated float      rep_float   = 30 [packed = true];
+    
+    repeated fixed64    rep_fixed64 = 31 [packed = true];
+    repeated sfixed64   rep_sfixed64= 32 [packed = true];
+    repeated double     rep_double  = 33 [packed = true];
+    
+    repeated string     rep_string  = 34;
+    repeated bytes      rep_bytes   = 35;
+    repeated SubMessage rep_submsg  = 36;
+    repeated MyEnum     rep_enum    = 37 [packed = true];
+    repeated EmptyMessage rep_emptymsg = 38;
+    
+    optional int32      opt_int32   = 41 [default = 4041];
+    optional int64      opt_int64   = 42 [default = 4042];
+    optional uint32     opt_uint32  = 43 [default = 4043];
+    optional uint64     opt_uint64  = 44 [default = 4044];
+    optional sint32     opt_sint32  = 45 [default = 4045];
+    optional sint64     opt_sint64  = 46 [default = 4046];
+    optional bool       opt_bool    = 47 [default = false];
+    
+    optional fixed32    opt_fixed32 = 48 [default = 4048];
+    optional sfixed32   opt_sfixed32= 49 [default = 4049];
+    optional float      opt_float   = 50 [default = 4050];
+    
+    optional fixed64    opt_fixed64 = 51 [default = 4051];
+    optional sfixed64   opt_sfixed64= 52 [default = 4052];
+    optional double     opt_double  = 53 [default = 4053];
+    
+    optional string     opt_string  = 54 [default = "4054"];
+    optional bytes      opt_bytes   = 55 [default = "4055"];
+    optional SubMessage opt_submsg  = 56;
+    optional MyEnum     opt_enum    = 57 [default = Second];
+    optional EmptyMessage opt_emptymsg = 58;
+
+    oneof oneof
+    {
+        SubMessage oneof_msg1 = 59;
+        EmptyMessage oneof_msg2 = 60;
+    }
+    
+    // Check that extreme integer values are handled correctly
+    required Limits     req_limits = 98;
+
+    // Just to make sure that the size of the fields has been calculated
+    // properly, i.e. otherwise a bug in last field might not be detected.
+    required int32      end = 99;
+
+
+    extensions 200 to 255;
+}
+
diff --git a/third_party/nanopb/tests/alltypes/decode_alltypes.c b/third_party/nanopb/tests/alltypes/decode_alltypes.c
new file mode 100644
index 0000000..458e511
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes/decode_alltypes.c
@@ -0,0 +1,221 @@
+/* Tests the decoding of all types.
+ * This is the counterpart of test_encode3.
+ * Run e.g. ./test_encode3 | ./test_decode3
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pb_decode.h>
+#include "alltypes.pb.h"
+#include "test_helpers.h"
+
+#define TEST(x) if (!(x)) { \
+    printf("Test " #x " failed.\n"); \
+    return false; \
+    }
+
+/* This function is called once from main(), it handles
+   the decoding and checks the fields. */
+bool check_alltypes(pb_istream_t *stream, int mode)
+{
+    /* Uses _init_default to just make sure that it works. */
+    AllTypes alltypes = AllTypes_init_default;
+    
+    /* Fill with garbage to better detect initialization errors */
+    memset(&alltypes, 0xAA, sizeof(alltypes));
+    alltypes.extensions = 0;
+    
+    if (!pb_decode(stream, AllTypes_fields, &alltypes))
+        return false;
+    
+    TEST(alltypes.req_int32         == -1001);
+    TEST(alltypes.req_int64         == -1002);
+    TEST(alltypes.req_uint32        == 1003);
+    TEST(alltypes.req_uint64        == 1004);
+    TEST(alltypes.req_sint32        == -1005);
+    TEST(alltypes.req_sint64        == -1006);
+    TEST(alltypes.req_bool          == true);
+    
+    TEST(alltypes.req_fixed32       == 1008);
+    TEST(alltypes.req_sfixed32      == -1009);
+    TEST(alltypes.req_float         == 1010.0f);
+    
+    TEST(alltypes.req_fixed64       == 1011);
+    TEST(alltypes.req_sfixed64      == -1012);
+    TEST(alltypes.req_double        == 1013.0f);
+    
+    TEST(strcmp(alltypes.req_string, "1014") == 0);
+    TEST(alltypes.req_bytes.size == 4);
+    TEST(memcmp(alltypes.req_bytes.bytes, "1015", 4) == 0);
+    TEST(strcmp(alltypes.req_submsg.substuff1, "1016") == 0);
+    TEST(alltypes.req_submsg.substuff2 == 1016);
+    TEST(alltypes.req_submsg.substuff3 == 3);
+    TEST(alltypes.req_enum == MyEnum_Truth);
+    
+    TEST(alltypes.rep_int32_count == 5 && alltypes.rep_int32[4] == -2001 && alltypes.rep_int32[0] == 0);
+    TEST(alltypes.rep_int64_count == 5 && alltypes.rep_int64[4] == -2002 && alltypes.rep_int64[0] == 0);
+    TEST(alltypes.rep_uint32_count == 5 && alltypes.rep_uint32[4] == 2003 && alltypes.rep_uint32[0] == 0);
+    TEST(alltypes.rep_uint64_count == 5 && alltypes.rep_uint64[4] == 2004 && alltypes.rep_uint64[0] == 0);
+    TEST(alltypes.rep_sint32_count == 5 && alltypes.rep_sint32[4] == -2005 && alltypes.rep_sint32[0] == 0);
+    TEST(alltypes.rep_sint64_count == 5 && alltypes.rep_sint64[4] == -2006 && alltypes.rep_sint64[0] == 0);
+    TEST(alltypes.rep_bool_count == 5 && alltypes.rep_bool[4] == true && alltypes.rep_bool[0] == false);
+    
+    TEST(alltypes.rep_fixed32_count == 5 && alltypes.rep_fixed32[4] == 2008 && alltypes.rep_fixed32[0] == 0);
+    TEST(alltypes.rep_sfixed32_count == 5 && alltypes.rep_sfixed32[4] == -2009 && alltypes.rep_sfixed32[0] == 0);
+    TEST(alltypes.rep_float_count == 5 && alltypes.rep_float[4] == 2010.0f && alltypes.rep_float[0] == 0.0f);
+    
+    TEST(alltypes.rep_fixed64_count == 5 && alltypes.rep_fixed64[4] == 2011 && alltypes.rep_fixed64[0] == 0);
+    TEST(alltypes.rep_sfixed64_count == 5 && alltypes.rep_sfixed64[4] == -2012 && alltypes.rep_sfixed64[0] == 0);
+    TEST(alltypes.rep_double_count == 5 && alltypes.rep_double[4] == 2013.0 && alltypes.rep_double[0] == 0.0);
+    
+    TEST(alltypes.rep_string_count == 5 && strcmp(alltypes.rep_string[4], "2014") == 0 && alltypes.rep_string[0][0] == '\0');
+    TEST(alltypes.rep_bytes_count == 5 && alltypes.rep_bytes[4].size == 4 && alltypes.rep_bytes[0].size == 0);
+    TEST(memcmp(alltypes.rep_bytes[4].bytes, "2015", 4) == 0);
+
+    TEST(alltypes.rep_submsg_count == 5);
+    TEST(strcmp(alltypes.rep_submsg[4].substuff1, "2016") == 0 && alltypes.rep_submsg[0].substuff1[0] == '\0');
+    TEST(alltypes.rep_submsg[4].substuff2 == 2016 && alltypes.rep_submsg[0].substuff2 == 0);
+    TEST(alltypes.rep_submsg[4].substuff3 == 2016 && alltypes.rep_submsg[0].substuff3 == 3);
+    
+    TEST(alltypes.rep_enum_count == 5 && alltypes.rep_enum[4] == MyEnum_Truth && alltypes.rep_enum[0] == MyEnum_Zero);
+    TEST(alltypes.rep_emptymsg_count == 5);
+    
+    if (mode == 0)
+    {
+        /* Expect default values */
+        TEST(alltypes.has_opt_int32     == false);
+        TEST(alltypes.opt_int32         == 4041);
+        TEST(alltypes.has_opt_int64     == false);
+        TEST(alltypes.opt_int64         == 4042);
+        TEST(alltypes.has_opt_uint32    == false);
+        TEST(alltypes.opt_uint32        == 4043);
+        TEST(alltypes.has_opt_uint64    == false);
+        TEST(alltypes.opt_uint64        == 4044);
+        TEST(alltypes.has_opt_sint32    == false);
+        TEST(alltypes.opt_sint32        == 4045);
+        TEST(alltypes.has_opt_sint64    == false);
+        TEST(alltypes.opt_sint64        == 4046);
+        TEST(alltypes.has_opt_bool      == false);
+        TEST(alltypes.opt_bool          == false);
+        
+        TEST(alltypes.has_opt_fixed32   == false);
+        TEST(alltypes.opt_fixed32       == 4048);
+        TEST(alltypes.has_opt_sfixed32  == false);
+        TEST(alltypes.opt_sfixed32      == 4049);
+        TEST(alltypes.has_opt_float     == false);
+        TEST(alltypes.opt_float         == 4050.0f);
+        
+        TEST(alltypes.has_opt_fixed64   == false);
+        TEST(alltypes.opt_fixed64       == 4051);
+        TEST(alltypes.has_opt_sfixed64  == false);
+        TEST(alltypes.opt_sfixed64      == 4052);
+        TEST(alltypes.has_opt_double    == false);
+        TEST(alltypes.opt_double        == 4053.0);
+        
+        TEST(alltypes.has_opt_string    == false);
+        TEST(strcmp(alltypes.opt_string, "4054") == 0);
+        TEST(alltypes.has_opt_bytes     == false);
+        TEST(alltypes.opt_bytes.size == 4);
+        TEST(memcmp(alltypes.opt_bytes.bytes, "4055", 4) == 0);
+        TEST(alltypes.has_opt_submsg    == false);
+        TEST(strcmp(alltypes.opt_submsg.substuff1, "1") == 0);
+        TEST(alltypes.opt_submsg.substuff2 == 2);
+        TEST(alltypes.opt_submsg.substuff3 == 3);
+        TEST(alltypes.has_opt_enum     == false);
+        TEST(alltypes.opt_enum == MyEnum_Second);
+        TEST(alltypes.has_opt_emptymsg == false);
+
+        TEST(alltypes.which_oneof == 0);
+    }
+    else
+    {
+        /* Expect filled-in values */
+        TEST(alltypes.has_opt_int32     == true);
+        TEST(alltypes.opt_int32         == 3041);
+        TEST(alltypes.has_opt_int64     == true);
+        TEST(alltypes.opt_int64         == 3042);
+        TEST(alltypes.has_opt_uint32    == true);
+        TEST(alltypes.opt_uint32        == 3043);
+        TEST(alltypes.has_opt_uint64    == true);
+        TEST(alltypes.opt_uint64        == 3044);
+        TEST(alltypes.has_opt_sint32    == true);
+        TEST(alltypes.opt_sint32        == 3045);
+        TEST(alltypes.has_opt_sint64    == true);
+        TEST(alltypes.opt_sint64        == 3046);
+        TEST(alltypes.has_opt_bool      == true);
+        TEST(alltypes.opt_bool          == true);
+        
+        TEST(alltypes.has_opt_fixed32   == true);
+        TEST(alltypes.opt_fixed32       == 3048);
+        TEST(alltypes.has_opt_sfixed32  == true);
+        TEST(alltypes.opt_sfixed32      == 3049);
+        TEST(alltypes.has_opt_float     == true);
+        TEST(alltypes.opt_float         == 3050.0f);
+        
+        TEST(alltypes.has_opt_fixed64   == true);
+        TEST(alltypes.opt_fixed64       == 3051);
+        TEST(alltypes.has_opt_sfixed64  == true);
+        TEST(alltypes.opt_sfixed64      == 3052);
+        TEST(alltypes.has_opt_double    == true);
+        TEST(alltypes.opt_double        == 3053.0);
+        
+        TEST(alltypes.has_opt_string    == true);
+        TEST(strcmp(alltypes.opt_string, "3054") == 0);
+        TEST(alltypes.has_opt_bytes     == true);
+        TEST(alltypes.opt_bytes.size == 4);
+        TEST(memcmp(alltypes.opt_bytes.bytes, "3055", 4) == 0);
+        TEST(alltypes.has_opt_submsg    == true);
+        TEST(strcmp(alltypes.opt_submsg.substuff1, "3056") == 0);
+        TEST(alltypes.opt_submsg.substuff2 == 3056);
+        TEST(alltypes.opt_submsg.substuff3 == 3);
+        TEST(alltypes.has_opt_enum      == true);
+        TEST(alltypes.opt_enum == MyEnum_Truth);
+        TEST(alltypes.has_opt_emptymsg  == true);
+
+        TEST(alltypes.which_oneof == AllTypes_oneof_msg1_tag);
+        TEST(strcmp(alltypes.oneof.oneof_msg1.substuff1, "4059") == 0);
+        TEST(alltypes.oneof.oneof_msg1.substuff2 == 4059);
+    }
+    
+    TEST(alltypes.req_limits.int32_min  == INT32_MIN);
+    TEST(alltypes.req_limits.int32_max  == INT32_MAX);
+    TEST(alltypes.req_limits.uint32_min == 0);
+    TEST(alltypes.req_limits.uint32_max == UINT32_MAX);
+    TEST(alltypes.req_limits.int64_min  == INT64_MIN);
+    TEST(alltypes.req_limits.int64_max  == INT64_MAX);
+    TEST(alltypes.req_limits.uint64_min == 0);
+    TEST(alltypes.req_limits.uint64_max == UINT64_MAX);
+    TEST(alltypes.req_limits.enum_min   == HugeEnum_Negative);
+    TEST(alltypes.req_limits.enum_max   == HugeEnum_Positive);
+    
+    TEST(alltypes.end == 1099);
+    
+    return true;
+}
+
+int main(int argc, char **argv)
+{
+    uint8_t buffer[1024];
+    size_t count;
+    pb_istream_t stream;
+
+    /* Whether to expect the optional values or the default values. */
+    int mode = (argc > 1) ? atoi(argv[1]) : 0;
+    
+    /* Read the data into buffer */
+    SET_BINARY_MODE(stdin);
+    count = fread(buffer, 1, sizeof(buffer), stdin);
+    
+    /* Construct a pb_istream_t for reading from the buffer */
+    stream = pb_istream_from_buffer(buffer, count);
+    
+    /* Decode and print out the stuff */
+    if (!check_alltypes(&stream, mode))
+    {
+        printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    } else {
+        return 0;
+    }
+}
diff --git a/third_party/nanopb/tests/alltypes/encode_alltypes.c b/third_party/nanopb/tests/alltypes/encode_alltypes.c
new file mode 100644
index 0000000..16f4b29
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes/encode_alltypes.c
@@ -0,0 +1,149 @@
+/* Attempts to test all the datatypes supported by ProtoBuf.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_encode.h>
+#include "alltypes.pb.h"
+#include "test_helpers.h"
+
+int main(int argc, char **argv)
+{
+    int mode = (argc > 1) ? atoi(argv[1]) : 0;
+    
+    /* Initialize the structure with constants */
+    AllTypes alltypes = AllTypes_init_zero;
+    
+    alltypes.req_int32         = -1001;
+    alltypes.req_int64         = -1002;
+    alltypes.req_uint32        = 1003;
+    alltypes.req_uint64        = 1004;
+    alltypes.req_sint32        = -1005;
+    alltypes.req_sint64        = -1006;
+    alltypes.req_bool          = true;
+    
+    alltypes.req_fixed32       = 1008;
+    alltypes.req_sfixed32      = -1009;
+    alltypes.req_float         = 1010.0f;
+    
+    alltypes.req_fixed64       = 1011;
+    alltypes.req_sfixed64      = -1012;
+    alltypes.req_double        = 1013.0;
+    
+    strcpy(alltypes.req_string, "1014");
+    alltypes.req_bytes.size = 4;
+    memcpy(alltypes.req_bytes.bytes, "1015", 4);
+    strcpy(alltypes.req_submsg.substuff1, "1016");
+    alltypes.req_submsg.substuff2 = 1016;
+    alltypes.req_enum = MyEnum_Truth;
+    
+    alltypes.rep_int32_count = 5; alltypes.rep_int32[4] = -2001;
+    alltypes.rep_int64_count = 5; alltypes.rep_int64[4] = -2002;
+    alltypes.rep_uint32_count = 5; alltypes.rep_uint32[4] = 2003;
+    alltypes.rep_uint64_count = 5; alltypes.rep_uint64[4] = 2004;
+    alltypes.rep_sint32_count = 5; alltypes.rep_sint32[4] = -2005;
+    alltypes.rep_sint64_count = 5; alltypes.rep_sint64[4] = -2006;
+    alltypes.rep_bool_count = 5; alltypes.rep_bool[4] = true;
+    
+    alltypes.rep_fixed32_count = 5; alltypes.rep_fixed32[4] = 2008;
+    alltypes.rep_sfixed32_count = 5; alltypes.rep_sfixed32[4] = -2009;
+    alltypes.rep_float_count = 5; alltypes.rep_float[4] = 2010.0f;
+    
+    alltypes.rep_fixed64_count = 5; alltypes.rep_fixed64[4] = 2011;
+    alltypes.rep_sfixed64_count = 5; alltypes.rep_sfixed64[4] = -2012;
+    alltypes.rep_double_count = 5; alltypes.rep_double[4] = 2013.0;
+    
+    alltypes.rep_string_count = 5; strcpy(alltypes.rep_string[4], "2014");
+    alltypes.rep_bytes_count = 5; alltypes.rep_bytes[4].size = 4;
+    memcpy(alltypes.rep_bytes[4].bytes, "2015", 4);
+
+    alltypes.rep_submsg_count = 5;
+    strcpy(alltypes.rep_submsg[4].substuff1, "2016");
+    alltypes.rep_submsg[4].substuff2 = 2016;
+    alltypes.rep_submsg[4].has_substuff3 = true;
+    alltypes.rep_submsg[4].substuff3 = 2016;
+    
+    alltypes.rep_enum_count = 5; alltypes.rep_enum[4] = MyEnum_Truth;
+    alltypes.rep_emptymsg_count = 5;
+    
+    alltypes.req_limits.int32_min  = INT32_MIN;
+    alltypes.req_limits.int32_max  = INT32_MAX;
+    alltypes.req_limits.uint32_min = 0;
+    alltypes.req_limits.uint32_max = UINT32_MAX;
+    alltypes.req_limits.int64_min  = INT64_MIN;
+    alltypes.req_limits.int64_max  = INT64_MAX;
+    alltypes.req_limits.uint64_min = 0;
+    alltypes.req_limits.uint64_max = UINT64_MAX;
+    alltypes.req_limits.enum_min   = HugeEnum_Negative;
+    alltypes.req_limits.enum_max   = HugeEnum_Positive;
+    
+    if (mode != 0)
+    {
+        /* Fill in values for optional fields */
+        alltypes.has_opt_int32 = true;
+        alltypes.opt_int32         = 3041;
+        alltypes.has_opt_int64 = true;
+        alltypes.opt_int64         = 3042;
+        alltypes.has_opt_uint32 = true;
+        alltypes.opt_uint32        = 3043;
+        alltypes.has_opt_uint64 = true;
+        alltypes.opt_uint64        = 3044;
+        alltypes.has_opt_sint32 = true;
+        alltypes.opt_sint32        = 3045;
+        alltypes.has_opt_sint64 = true;
+        alltypes.opt_sint64        = 3046;
+        alltypes.has_opt_bool = true;
+        alltypes.opt_bool          = true;
+        
+        alltypes.has_opt_fixed32 = true;
+        alltypes.opt_fixed32       = 3048;
+        alltypes.has_opt_sfixed32 = true;
+        alltypes.opt_sfixed32      = 3049;
+        alltypes.has_opt_float = true;
+        alltypes.opt_float         = 3050.0f;
+        
+        alltypes.has_opt_fixed64 = true;
+        alltypes.opt_fixed64       = 3051;
+        alltypes.has_opt_sfixed64 = true;
+        alltypes.opt_sfixed64      = 3052;
+        alltypes.has_opt_double = true;
+        alltypes.opt_double        = 3053.0;
+        
+        alltypes.has_opt_string = true;
+        strcpy(alltypes.opt_string, "3054");
+        alltypes.has_opt_bytes = true;
+        alltypes.opt_bytes.size = 4;
+        memcpy(alltypes.opt_bytes.bytes, "3055", 4);
+        alltypes.has_opt_submsg = true;
+        strcpy(alltypes.opt_submsg.substuff1, "3056");
+        alltypes.opt_submsg.substuff2 = 3056;
+        alltypes.has_opt_enum = true;
+        alltypes.opt_enum = MyEnum_Truth;
+        alltypes.has_opt_emptymsg = true;
+
+        alltypes.which_oneof = AllTypes_oneof_msg1_tag;
+        strcpy(alltypes.oneof.oneof_msg1.substuff1, "4059");
+        alltypes.oneof.oneof_msg1.substuff2 = 4059;
+    }
+    
+    alltypes.end = 1099;
+    
+    {
+        uint8_t buffer[AllTypes_size];
+        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+        
+        /* Now encode it and check if we succeeded. */
+        if (pb_encode(&stream, AllTypes_fields, &alltypes))
+        {
+            SET_BINARY_MODE(stdout);
+            fwrite(buffer, 1, stream.bytes_written, stdout);
+            return 0; /* Success */
+        }
+        else
+        {
+            fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
+            return 1; /* Failure */
+        }
+    }
+}
diff --git a/third_party/nanopb/tests/alltypes_callback/SConscript b/third_party/nanopb/tests/alltypes_callback/SConscript
new file mode 100644
index 0000000..a241f24
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes_callback/SConscript
@@ -0,0 +1,23 @@
+# Test the AllTypes encoding & decoding using callbacks for all fields.
+
+Import("env")
+
+c = Copy("$TARGET", "$SOURCE")
+env.Command("alltypes.proto", "#alltypes/alltypes.proto", c)
+
+env.NanopbProto(["alltypes", "alltypes.options"])
+enc = env.Program(["encode_alltypes_callback.c", "alltypes.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"])
+dec = env.Program(["decode_alltypes_callback.c", "alltypes.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"])
+
+refdec = "$BUILD/alltypes/decode_alltypes$PROGSUFFIX"
+
+# Encode and compare results
+env.RunTest(enc)
+env.RunTest("decode_alltypes.output", [refdec, "encode_alltypes_callback.output"])
+env.RunTest("decode_alltypes_callback.output", [dec, "encode_alltypes_callback.output"])
+
+# Do the same thing with the optional fields present
+env.RunTest("optionals.output", enc, ARGS = ['1'])
+env.RunTest("optionals.refdecout", [refdec, "optionals.output"], ARGS = ['1'])
+env.RunTest("optionals.decout", [dec, "optionals.output"], ARGS = ['1'])
+
diff --git a/third_party/nanopb/tests/alltypes_callback/alltypes.options b/third_party/nanopb/tests/alltypes_callback/alltypes.options
new file mode 100644
index 0000000..daee522
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes_callback/alltypes.options
@@ -0,0 +1,4 @@
+# Generate all fields as callbacks.
+AllTypes.*              type:FT_CALLBACK
+SubMessage.substuff1    max_size:16
+AllTypes.oneof          no_unions:true
diff --git a/third_party/nanopb/tests/alltypes_callback/decode_alltypes_callback.c b/third_party/nanopb/tests/alltypes_callback/decode_alltypes_callback.c
new file mode 100644
index 0000000..c53ab6e
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes_callback/decode_alltypes_callback.c
@@ -0,0 +1,429 @@
+/* Attempts to test all the datatypes supported by ProtoBuf when used as callback fields.
+ * Note that normally there would be no reason to use callback fields for this,
+ * because each encoder defined here only gives a single field.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pb_decode.h>
+#include "alltypes.pb.h"
+#include "test_helpers.h"
+
+#define TEST(x) if (!(x)) { \
+    printf("Test " #x " failed (in field %d).\n", field->tag); \
+    return false; \
+    }
+
+static bool read_varint(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    uint64_t value;
+    if (!pb_decode_varint(stream, &value))
+        return false;
+    
+    TEST((int64_t)value == (long)*arg);
+    return true;
+}
+
+static bool read_svarint(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    int64_t value;
+    if (!pb_decode_svarint(stream, &value))
+        return false;
+    
+    TEST(value == (long)*arg);
+    return true;
+}
+
+static bool read_fixed32(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    uint32_t value;
+    if (!pb_decode_fixed32(stream, &value))
+        return false;
+    
+    TEST(value == *(uint32_t*)*arg);
+    return true;
+}
+
+static bool read_fixed64(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    uint64_t value;
+    if (!pb_decode_fixed64(stream, &value))
+        return false;
+    
+    TEST(value == *(uint64_t*)*arg);
+    return true;
+}
+
+static bool read_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    uint8_t buf[16] = {0};
+    size_t len = stream->bytes_left;
+    
+    if (len > sizeof(buf) - 1 || !pb_read(stream, buf, len))
+        return false;
+    
+    TEST(strcmp((char*)buf, *arg) == 0);
+    return true;
+}
+
+static bool read_submsg(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    SubMessage submsg = {""};
+    
+    if (!pb_decode(stream, SubMessage_fields, &submsg))
+        return false;
+    
+    TEST(memcmp(&submsg, *arg, sizeof(submsg)));
+    return true;
+}
+
+static bool read_emptymsg(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    EmptyMessage emptymsg = {0};
+    return pb_decode(stream, EmptyMessage_fields, &emptymsg);
+}
+
+static bool read_repeated_varint(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    int32_t** expected = (int32_t**)arg;
+    uint64_t value;
+    if (!pb_decode_varint(stream, &value))
+        return false;
+
+    TEST(*(*expected)++ == value);
+    return true;
+}
+
+static bool read_repeated_svarint(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    int32_t** expected = (int32_t**)arg;
+    int64_t value;
+    if (!pb_decode_svarint(stream, &value))
+        return false;
+
+    TEST(*(*expected)++ == value);
+    return true;
+}
+
+static bool read_repeated_fixed32(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    uint32_t** expected = (uint32_t**)arg;
+    uint32_t value;
+    if (!pb_decode_fixed32(stream, &value))
+        return false;
+
+    TEST(*(*expected)++ == value);
+    return true;
+}
+
+static bool read_repeated_fixed64(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    uint64_t** expected = (uint64_t**)arg;
+    uint64_t value;
+    if (!pb_decode_fixed64(stream, &value))
+        return false;
+
+    TEST(*(*expected)++ == value);
+    return true;
+}
+
+static bool read_repeated_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    uint8_t*** expected = (uint8_t***)arg;
+    uint8_t buf[16] = {0};
+    size_t len = stream->bytes_left;
+    
+    if (len > sizeof(buf) - 1 || !pb_read(stream, buf, len))
+        return false;
+    
+    TEST(strcmp((char*)*(*expected)++, (char*)buf) == 0);
+    return true;
+}
+
+static bool read_repeated_submsg(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    SubMessage** expected = (SubMessage**)arg;
+    SubMessage decoded = {""};
+    if (!pb_decode(stream, SubMessage_fields, &decoded))
+        return false;
+
+    TEST(memcmp((*expected)++, &decoded, sizeof(decoded)) == 0);
+    return true;
+}
+
+static bool read_limits(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    Limits decoded = {0};
+    if (!pb_decode(stream, Limits_fields, &decoded))
+        return false;
+
+    TEST(decoded.int32_min  == INT32_MIN);
+    TEST(decoded.int32_max  == INT32_MAX);
+    TEST(decoded.uint32_min == 0);
+    TEST(decoded.uint32_max == UINT32_MAX);
+    TEST(decoded.int64_min  == INT64_MIN);
+    TEST(decoded.int64_max  == INT64_MAX);
+    TEST(decoded.uint64_min == 0);
+    TEST(decoded.uint64_max == UINT64_MAX);
+    TEST(decoded.enum_min   == HugeEnum_Negative);
+    TEST(decoded.enum_max   == HugeEnum_Positive);
+    
+    return true;
+}
+
+/* This function is called once from main(), it handles
+   the decoding and checks the fields. */
+bool check_alltypes(pb_istream_t *stream, int mode)
+{
+    /* Values for use from callbacks through pointers. */
+    uint32_t    req_fixed32     = 1008;
+    int32_t     req_sfixed32    = -1009;
+    float       req_float       = 1010.0f;
+    uint64_t    req_fixed64     = 1011;
+    int64_t     req_sfixed64    = -1012;
+    double      req_double      = 1013.0;
+    SubMessage  req_submsg      = {"1016", 1016};
+    
+    int32_t     rep_int32[5]    = {0, 0, 0, 0, -2001};
+    int32_t     rep_int64[5]    = {0, 0, 0, 0, -2002};
+    int32_t     rep_uint32[5]   = {0, 0, 0, 0,  2003};
+    int32_t     rep_uint64[5]   = {0, 0, 0, 0,  2004};
+    int32_t     rep_sint32[5]   = {0, 0, 0, 0, -2005};
+    int32_t     rep_sint64[5]   = {0, 0, 0, 0, -2006};
+    int32_t     rep_bool[5]     = {false, false, false, false, true};
+    uint32_t    rep_fixed32[5]  = {0, 0, 0, 0,  2008};
+    int32_t     rep_sfixed32[5] = {0, 0, 0, 0, -2009};
+    float       rep_float[5]    = {0, 0, 0, 0,  2010.0f};
+    uint64_t    rep_fixed64[5]  = {0, 0, 0, 0,  2011};
+    int64_t     rep_sfixed64[5] = {0, 0, 0, 0, -2012};
+    double      rep_double[5]   = {0, 0, 0, 0,  2013.0};
+    char*       rep_string[5]   = {"", "", "", "", "2014"};
+    char*       rep_bytes[5]    = {"", "", "", "", "2015"};
+    SubMessage  rep_submsg[5]   = {{"", 0, 0, 3},
+                                   {"", 0, 0, 3},
+                                   {"", 0, 0, 3},
+                                   {"", 0, 0, 3},
+                                   {"2016", 2016, true, 2016}};
+    int32_t     rep_enum[5]     = {0, 0, 0, 0, MyEnum_Truth};
+    
+    uint32_t    opt_fixed32     = 3048;
+    int32_t     opt_sfixed32    = 3049;
+    float       opt_float       = 3050.0f;
+    uint64_t    opt_fixed64     = 3051;
+    int64_t     opt_sfixed64    = 3052;
+    double      opt_double      = 3053.0f;
+    SubMessage  opt_submsg      = {"3056", 3056};
+
+    SubMessage  oneof_msg1      = {"4059", 4059};
+    
+    /* Bind callbacks for required fields */
+    AllTypes alltypes;
+    
+    /* Fill with garbage to better detect initialization errors */
+    memset(&alltypes, 0xAA, sizeof(alltypes));
+    alltypes.extensions = 0;
+    
+    alltypes.req_int32.funcs.decode = &read_varint;
+    alltypes.req_int32.arg = (void*)-1001;
+    
+    alltypes.req_int64.funcs.decode = &read_varint;
+    alltypes.req_int64.arg = (void*)-1002;
+    
+    alltypes.req_uint32.funcs.decode = &read_varint;
+    alltypes.req_uint32.arg = (void*)1003;
+
+    alltypes.req_uint32.funcs.decode = &read_varint;
+    alltypes.req_uint32.arg = (void*)1003;    
+    
+    alltypes.req_uint64.funcs.decode = &read_varint;
+    alltypes.req_uint64.arg = (void*)1004;
+    
+    alltypes.req_sint32.funcs.decode = &read_svarint;
+    alltypes.req_sint32.arg = (void*)-1005;   
+    
+    alltypes.req_sint64.funcs.decode = &read_svarint;
+    alltypes.req_sint64.arg = (void*)-1006;   
+    
+    alltypes.req_bool.funcs.decode = &read_varint;
+    alltypes.req_bool.arg = (void*)true;   
+    
+    alltypes.req_fixed32.funcs.decode = &read_fixed32;
+    alltypes.req_fixed32.arg = &req_fixed32;
+    
+    alltypes.req_sfixed32.funcs.decode = &read_fixed32;
+    alltypes.req_sfixed32.arg = &req_sfixed32;
+    
+    alltypes.req_float.funcs.decode = &read_fixed32;
+    alltypes.req_float.arg = &req_float;
+    
+    alltypes.req_fixed64.funcs.decode = &read_fixed64;
+    alltypes.req_fixed64.arg = &req_fixed64;
+    
+    alltypes.req_sfixed64.funcs.decode = &read_fixed64;
+    alltypes.req_sfixed64.arg = &req_sfixed64;
+    
+    alltypes.req_double.funcs.decode = &read_fixed64;
+    alltypes.req_double.arg = &req_double;
+    
+    alltypes.req_string.funcs.decode = &read_string;
+    alltypes.req_string.arg = "1014";
+    
+    alltypes.req_bytes.funcs.decode = &read_string;
+    alltypes.req_bytes.arg = "1015";
+    
+    alltypes.req_submsg.funcs.decode = &read_submsg;
+    alltypes.req_submsg.arg = &req_submsg;
+    
+    alltypes.req_enum.funcs.decode = &read_varint;
+    alltypes.req_enum.arg = (void*)MyEnum_Truth;
+    
+    alltypes.req_emptymsg.funcs.decode = &read_emptymsg;
+    
+    /* Bind callbacks for repeated fields */
+    alltypes.rep_int32.funcs.decode = &read_repeated_varint;
+    alltypes.rep_int32.arg = rep_int32;
+    
+    alltypes.rep_int64.funcs.decode = &read_repeated_varint;
+    alltypes.rep_int64.arg = rep_int64;
+    
+    alltypes.rep_uint32.funcs.decode = &read_repeated_varint;
+    alltypes.rep_uint32.arg = rep_uint32;
+    
+    alltypes.rep_uint64.funcs.decode = &read_repeated_varint;
+    alltypes.rep_uint64.arg = rep_uint64;
+    
+    alltypes.rep_sint32.funcs.decode = &read_repeated_svarint;
+    alltypes.rep_sint32.arg = rep_sint32;
+    
+    alltypes.rep_sint64.funcs.decode = &read_repeated_svarint;
+    alltypes.rep_sint64.arg = rep_sint64;
+    
+    alltypes.rep_bool.funcs.decode = &read_repeated_varint;
+    alltypes.rep_bool.arg = rep_bool;
+    
+    alltypes.rep_fixed32.funcs.decode = &read_repeated_fixed32;
+    alltypes.rep_fixed32.arg = rep_fixed32;
+    
+    alltypes.rep_sfixed32.funcs.decode = &read_repeated_fixed32;
+    alltypes.rep_sfixed32.arg = rep_sfixed32;
+    
+    alltypes.rep_float.funcs.decode = &read_repeated_fixed32;
+    alltypes.rep_float.arg = rep_float;
+    
+    alltypes.rep_fixed64.funcs.decode = &read_repeated_fixed64;
+    alltypes.rep_fixed64.arg = rep_fixed64;
+    
+    alltypes.rep_sfixed64.funcs.decode = &read_repeated_fixed64;
+    alltypes.rep_sfixed64.arg = rep_sfixed64;
+    
+    alltypes.rep_double.funcs.decode = &read_repeated_fixed64;
+    alltypes.rep_double.arg = rep_double;
+    
+    alltypes.rep_string.funcs.decode = &read_repeated_string;
+    alltypes.rep_string.arg = rep_string;
+    
+    alltypes.rep_bytes.funcs.decode = &read_repeated_string;
+    alltypes.rep_bytes.arg = rep_bytes;
+    
+    alltypes.rep_submsg.funcs.decode = &read_repeated_submsg;
+    alltypes.rep_submsg.arg = rep_submsg;
+    
+    alltypes.rep_enum.funcs.decode = &read_repeated_varint;
+    alltypes.rep_enum.arg = rep_enum;
+    
+    alltypes.rep_emptymsg.funcs.decode = &read_emptymsg;
+    
+    alltypes.req_limits.funcs.decode = &read_limits;
+    
+    alltypes.end.funcs.decode = &read_varint;
+    alltypes.end.arg = (void*)1099;
+    
+    /* Bind callbacks for optional fields */
+    if (mode == 1)
+    {
+        alltypes.opt_int32.funcs.decode = &read_varint;
+        alltypes.opt_int32.arg = (void*)3041;
+        
+        alltypes.opt_int64.funcs.decode = &read_varint;
+        alltypes.opt_int64.arg = (void*)3042;
+        
+        alltypes.opt_uint32.funcs.decode = &read_varint;
+        alltypes.opt_uint32.arg = (void*)3043;
+        
+        alltypes.opt_uint64.funcs.decode = &read_varint;
+        alltypes.opt_uint64.arg = (void*)3044;
+        
+        alltypes.opt_sint32.funcs.decode = &read_svarint;
+        alltypes.opt_sint32.arg = (void*)3045;
+        
+        alltypes.opt_sint64.funcs.decode = &read_svarint;
+        alltypes.opt_sint64.arg = (void*)3046;
+        
+        alltypes.opt_bool.funcs.decode = &read_varint;
+        alltypes.opt_bool.arg = (void*)true;
+
+        alltypes.opt_fixed32.funcs.decode = &read_fixed32;
+        alltypes.opt_fixed32.arg = &opt_fixed32;
+        
+        alltypes.opt_sfixed32.funcs.decode = &read_fixed32;
+        alltypes.opt_sfixed32.arg = &opt_sfixed32;
+        
+        alltypes.opt_float.funcs.decode = &read_fixed32;
+        alltypes.opt_float.arg = &opt_float;
+        
+        alltypes.opt_fixed64.funcs.decode = &read_fixed64;
+        alltypes.opt_fixed64.arg = &opt_fixed64;
+        
+        alltypes.opt_sfixed64.funcs.decode = &read_fixed64;
+        alltypes.opt_sfixed64.arg = &opt_sfixed64;
+        
+        alltypes.opt_double.funcs.decode = &read_fixed64;
+        alltypes.opt_double.arg = &opt_double;
+        
+        alltypes.opt_string.funcs.decode = &read_string;
+        alltypes.opt_string.arg = "3054";
+        
+        alltypes.opt_bytes.funcs.decode = &read_string;
+        alltypes.opt_bytes.arg = "3055";
+        
+        alltypes.opt_submsg.funcs.decode = &read_submsg;
+        alltypes.opt_submsg.arg = &opt_submsg;
+        
+        alltypes.opt_enum.funcs.decode = &read_varint;
+        alltypes.opt_enum.arg = (void*)MyEnum_Truth;
+        
+        alltypes.opt_emptymsg.funcs.decode = &read_emptymsg;
+
+        alltypes.oneof_msg1.funcs.decode = &read_submsg;
+        alltypes.oneof_msg1.arg = &oneof_msg1;
+    }
+    
+    return pb_decode(stream, AllTypes_fields, &alltypes);
+}
+
+int main(int argc, char **argv)
+{
+    uint8_t buffer[1024];
+    size_t count;
+    pb_istream_t stream;
+
+    /* Whether to expect the optional values or the default values. */
+    int mode = (argc > 1) ? atoi(argv[1]) : 0;
+    
+    /* Read the data into buffer */
+    SET_BINARY_MODE(stdin);
+    count = fread(buffer, 1, sizeof(buffer), stdin);
+    
+    /* Construct a pb_istream_t for reading from the buffer */
+    stream = pb_istream_from_buffer(buffer, count);
+    
+    /* Decode and print out the stuff */
+    if (!check_alltypes(&stream, mode))
+    {
+        printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    } else {
+        return 0;
+    }
+}
diff --git a/third_party/nanopb/tests/alltypes_callback/encode_alltypes_callback.c b/third_party/nanopb/tests/alltypes_callback/encode_alltypes_callback.c
new file mode 100644
index 0000000..abc43f5
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes_callback/encode_alltypes_callback.c
@@ -0,0 +1,402 @@
+/* Attempts to test all the datatypes supported by ProtoBuf when used as callback fields.
+ * Note that normally there would be no reason to use callback fields for this,
+ * because each encoder defined here only gives a single field.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_encode.h>
+#include "alltypes.pb.h"
+#include "test_helpers.h"
+
+static bool write_varint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_varint(stream, (long)*arg);
+}
+
+static bool write_svarint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_svarint(stream, (long)*arg);
+}
+
+static bool write_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_fixed32(stream, *arg);
+}
+
+static bool write_fixed64(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_fixed64(stream, *arg);
+}
+
+static bool write_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_string(stream, *arg, strlen(*arg));
+}
+
+static bool write_submsg(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+   
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, SubMessage_fields, *arg);
+}
+
+static bool write_emptymsg(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    EmptyMessage emptymsg = {0};
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, EmptyMessage_fields, &emptymsg);
+}
+
+static bool write_repeated_varint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_varint(stream, 0) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_varint(stream, 0) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_varint(stream, 0) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_varint(stream, 0) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_varint(stream, (long)*arg);
+}
+
+static bool write_repeated_svarint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_svarint(stream, 0) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_svarint(stream, 0) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_svarint(stream, 0) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_svarint(stream, 0) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_svarint(stream, (long)*arg);
+}
+
+static bool write_repeated_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    uint32_t dummy = 0;
+
+    /* Make it a packed field */
+    return pb_encode_tag(stream, PB_WT_STRING, field->tag) &&
+           pb_encode_varint(stream, 5 * 4) && /* Number of bytes */
+           pb_encode_fixed32(stream, &dummy) &&
+           pb_encode_fixed32(stream, &dummy) &&
+           pb_encode_fixed32(stream, &dummy) &&
+           pb_encode_fixed32(stream, &dummy) &&
+           pb_encode_fixed32(stream, *arg);
+}
+
+static bool write_repeated_fixed64(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    uint64_t dummy = 0;
+
+    /* Make it a packed field */
+    return pb_encode_tag(stream, PB_WT_STRING, field->tag) &&
+           pb_encode_varint(stream, 5 * 8) && /* Number of bytes */
+           pb_encode_fixed64(stream, &dummy) &&
+           pb_encode_fixed64(stream, &dummy) &&
+           pb_encode_fixed64(stream, &dummy) &&
+           pb_encode_fixed64(stream, &dummy) &&
+           pb_encode_fixed64(stream, *arg);
+}
+
+static bool write_repeated_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_string(stream, 0, 0) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_string(stream, 0, 0) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_string(stream, 0, 0) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_string(stream, 0, 0) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_string(stream, *arg, strlen(*arg));
+}
+
+static bool write_repeated_submsg(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    SubMessage dummy = {""};
+
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, SubMessage_fields, &dummy) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, SubMessage_fields, &dummy) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, SubMessage_fields, &dummy) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, SubMessage_fields, &dummy) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, SubMessage_fields, *arg);
+}
+
+static bool write_limits(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    Limits limits = {0};
+    limits.int32_min  = INT32_MIN;
+    limits.int32_max  = INT32_MAX;
+    limits.uint32_min = 0;
+    limits.uint32_max = UINT32_MAX;
+    limits.int64_min  = INT64_MIN;
+    limits.int64_max  = INT64_MAX;
+    limits.uint64_min = 0;
+    limits.uint64_max = UINT64_MAX;
+    limits.enum_min   = HugeEnum_Negative;
+    limits.enum_max   = HugeEnum_Positive;
+   
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, Limits_fields, &limits);
+}
+
+static bool write_repeated_emptymsg(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    EmptyMessage emptymsg = {0};
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, EmptyMessage_fields, &emptymsg) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, EmptyMessage_fields, &emptymsg) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, EmptyMessage_fields, &emptymsg) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, EmptyMessage_fields, &emptymsg) &&
+           pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, EmptyMessage_fields, &emptymsg);
+}
+
+int main(int argc, char **argv)
+{
+    int mode = (argc > 1) ? atoi(argv[1]) : 0;
+
+    /* Values for use from callbacks through pointers. */
+    uint32_t    req_fixed32     = 1008;
+    int32_t     req_sfixed32    = -1009;
+    float       req_float       = 1010.0f;
+    uint64_t    req_fixed64     = 1011;
+    int64_t     req_sfixed64    = -1012;
+    double      req_double      = 1013.0;
+    SubMessage  req_submsg      = {"1016", 1016};
+    
+    uint32_t    rep_fixed32     = 2008;
+    int32_t     rep_sfixed32    = -2009;
+    float       rep_float       = 2010.0f;
+    uint64_t    rep_fixed64     = 2011;
+    int64_t     rep_sfixed64    = -2012;
+    double      rep_double      = 2013.0;
+    SubMessage  rep_submsg      = {"2016", 2016, true, 2016};
+    
+    uint32_t    opt_fixed32     = 3048;
+    int32_t     opt_sfixed32    = 3049;
+    float       opt_float       = 3050.0f;
+    uint64_t    opt_fixed64     = 3051;
+    int64_t     opt_sfixed64    = 3052;
+    double      opt_double      = 3053.0f;
+    SubMessage  opt_submsg      = {"3056", 3056};
+    
+    SubMessage  oneof_msg1      = {"4059", 4059};
+
+    /* Bind callbacks for required fields */
+    AllTypes alltypes = {{{0}}};
+    
+    alltypes.req_int32.funcs.encode = &write_varint;
+    alltypes.req_int32.arg = (void*)-1001;
+    
+    alltypes.req_int64.funcs.encode = &write_varint;
+    alltypes.req_int64.arg = (void*)-1002;
+    
+    alltypes.req_uint32.funcs.encode = &write_varint;
+    alltypes.req_uint32.arg = (void*)1003;
+
+    alltypes.req_uint32.funcs.encode = &write_varint;
+    alltypes.req_uint32.arg = (void*)1003;    
+    
+    alltypes.req_uint64.funcs.encode = &write_varint;
+    alltypes.req_uint64.arg = (void*)1004;
+    
+    alltypes.req_sint32.funcs.encode = &write_svarint;
+    alltypes.req_sint32.arg = (void*)-1005;   
+    
+    alltypes.req_sint64.funcs.encode = &write_svarint;
+    alltypes.req_sint64.arg = (void*)-1006;   
+    
+    alltypes.req_bool.funcs.encode = &write_varint;
+    alltypes.req_bool.arg = (void*)true;   
+    
+    alltypes.req_fixed32.funcs.encode = &write_fixed32;
+    alltypes.req_fixed32.arg = &req_fixed32;
+    
+    alltypes.req_sfixed32.funcs.encode = &write_fixed32;
+    alltypes.req_sfixed32.arg = &req_sfixed32;
+    
+    alltypes.req_float.funcs.encode = &write_fixed32;
+    alltypes.req_float.arg = &req_float;
+    
+    alltypes.req_fixed64.funcs.encode = &write_fixed64;
+    alltypes.req_fixed64.arg = &req_fixed64;
+    
+    alltypes.req_sfixed64.funcs.encode = &write_fixed64;
+    alltypes.req_sfixed64.arg = &req_sfixed64;
+    
+    alltypes.req_double.funcs.encode = &write_fixed64;
+    alltypes.req_double.arg = &req_double;
+    
+    alltypes.req_string.funcs.encode = &write_string;
+    alltypes.req_string.arg = "1014";
+    
+    alltypes.req_bytes.funcs.encode = &write_string;
+    alltypes.req_bytes.arg = "1015";
+    
+    alltypes.req_submsg.funcs.encode = &write_submsg;
+    alltypes.req_submsg.arg = &req_submsg;
+    
+    alltypes.req_enum.funcs.encode = &write_varint;
+    alltypes.req_enum.arg = (void*)MyEnum_Truth;
+    
+    alltypes.req_emptymsg.funcs.encode = &write_emptymsg;
+    
+    /* Bind callbacks for repeated fields */
+    alltypes.rep_int32.funcs.encode = &write_repeated_varint;
+    alltypes.rep_int32.arg = (void*)-2001;
+    
+    alltypes.rep_int64.funcs.encode = &write_repeated_varint;
+    alltypes.rep_int64.arg = (void*)-2002;
+    
+    alltypes.rep_uint32.funcs.encode = &write_repeated_varint;
+    alltypes.rep_uint32.arg = (void*)2003;
+    
+    alltypes.rep_uint64.funcs.encode = &write_repeated_varint;
+    alltypes.rep_uint64.arg = (void*)2004;
+    
+    alltypes.rep_sint32.funcs.encode = &write_repeated_svarint;
+    alltypes.rep_sint32.arg = (void*)-2005;
+    
+    alltypes.rep_sint64.funcs.encode = &write_repeated_svarint;
+    alltypes.rep_sint64.arg = (void*)-2006;
+    
+    alltypes.rep_bool.funcs.encode = &write_repeated_varint;
+    alltypes.rep_bool.arg = (void*)true;
+    
+    alltypes.rep_fixed32.funcs.encode = &write_repeated_fixed32;
+    alltypes.rep_fixed32.arg = &rep_fixed32;
+
+    alltypes.rep_sfixed32.funcs.encode = &write_repeated_fixed32;
+    alltypes.rep_sfixed32.arg = &rep_sfixed32;
+    
+    alltypes.rep_float.funcs.encode = &write_repeated_fixed32;
+    alltypes.rep_float.arg = &rep_float;
+    
+    alltypes.rep_fixed64.funcs.encode = &write_repeated_fixed64;
+    alltypes.rep_fixed64.arg = &rep_fixed64;
+
+    alltypes.rep_sfixed64.funcs.encode = &write_repeated_fixed64;
+    alltypes.rep_sfixed64.arg = &rep_sfixed64;
+    
+    alltypes.rep_double.funcs.encode = &write_repeated_fixed64;
+    alltypes.rep_double.arg = &rep_double;
+    
+    alltypes.rep_string.funcs.encode = &write_repeated_string;
+    alltypes.rep_string.arg = "2014";
+    
+    alltypes.rep_bytes.funcs.encode = &write_repeated_string;
+    alltypes.rep_bytes.arg = "2015";
+    
+    alltypes.rep_submsg.funcs.encode = &write_repeated_submsg;
+    alltypes.rep_submsg.arg = &rep_submsg;
+    
+    alltypes.rep_enum.funcs.encode = &write_repeated_varint;
+    alltypes.rep_enum.arg = (void*)MyEnum_Truth;
+    
+    alltypes.rep_emptymsg.funcs.encode = &write_repeated_emptymsg;
+    
+    alltypes.req_limits.funcs.encode = &write_limits;
+    
+    /* Bind callbacks for optional fields */
+    if (mode != 0)
+    {
+        alltypes.opt_int32.funcs.encode = &write_varint;
+        alltypes.opt_int32.arg = (void*)3041;
+        
+        alltypes.opt_int64.funcs.encode = &write_varint;
+        alltypes.opt_int64.arg = (void*)3042;
+        
+        alltypes.opt_uint32.funcs.encode = &write_varint;
+        alltypes.opt_uint32.arg = (void*)3043;
+        
+        alltypes.opt_uint64.funcs.encode = &write_varint;
+        alltypes.opt_uint64.arg = (void*)3044;
+        
+        alltypes.opt_sint32.funcs.encode = &write_svarint;
+        alltypes.opt_sint32.arg = (void*)3045;
+        
+        alltypes.opt_sint64.funcs.encode = &write_svarint;
+        alltypes.opt_sint64.arg = (void*)3046;
+        
+        alltypes.opt_bool.funcs.encode = &write_varint;
+        alltypes.opt_bool.arg = (void*)true;
+
+        alltypes.opt_fixed32.funcs.encode = &write_fixed32;
+        alltypes.opt_fixed32.arg = &opt_fixed32;
+        
+        alltypes.opt_sfixed32.funcs.encode = &write_fixed32;
+        alltypes.opt_sfixed32.arg = &opt_sfixed32;
+        
+        alltypes.opt_float.funcs.encode = &write_fixed32;
+        alltypes.opt_float.arg = &opt_float;
+        
+        alltypes.opt_fixed64.funcs.encode = &write_fixed64;
+        alltypes.opt_fixed64.arg = &opt_fixed64;
+        
+        alltypes.opt_sfixed64.funcs.encode = &write_fixed64;
+        alltypes.opt_sfixed64.arg = &opt_sfixed64;
+        
+        alltypes.opt_double.funcs.encode = &write_fixed64;
+        alltypes.opt_double.arg = &opt_double;
+        
+        alltypes.opt_string.funcs.encode = &write_string;
+        alltypes.opt_string.arg = "3054";
+        
+        alltypes.opt_bytes.funcs.encode = &write_string;
+        alltypes.opt_bytes.arg = "3055";
+        
+        alltypes.opt_submsg.funcs.encode = &write_submsg;
+        alltypes.opt_submsg.arg = &opt_submsg;
+        
+        alltypes.opt_enum.funcs.encode = &write_varint;
+        alltypes.opt_enum.arg = (void*)MyEnum_Truth;
+        
+        alltypes.opt_emptymsg.funcs.encode = &write_emptymsg;
+
+        alltypes.oneof_msg1.funcs.encode = &write_submsg;
+        alltypes.oneof_msg1.arg = &oneof_msg1;
+    }
+    
+    alltypes.end.funcs.encode = &write_varint;
+    alltypes.end.arg = (void*)1099;
+    
+    {
+        uint8_t buffer[2048];
+        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+        
+        /* Now encode it and check if we succeeded. */
+        if (pb_encode(&stream, AllTypes_fields, &alltypes))
+        {
+            SET_BINARY_MODE(stdout);
+            fwrite(buffer, 1, stream.bytes_written, stdout);
+            return 0; /* Success */
+        }
+        else
+        {
+            fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
+            return 1; /* Failure */
+        }
+    }
+}
diff --git a/third_party/nanopb/tests/alltypes_pointer/SConscript b/third_party/nanopb/tests/alltypes_pointer/SConscript
new file mode 100644
index 0000000..b095ae0
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes_pointer/SConscript
@@ -0,0 +1,40 @@
+# Encode the AllTypes message using pointers for all fields, and verify the
+# output against the normal AllTypes test case.
+
+Import("env", "malloc_env")
+
+c = Copy("$TARGET", "$SOURCE")
+env.Command("alltypes.proto", "#alltypes/alltypes.proto", c)
+
+env.NanopbProto(["alltypes", "alltypes.options"])
+enc = malloc_env.Program(["encode_alltypes_pointer.c",
+                          "alltypes.pb.c",
+                          "$COMMON/pb_encode_with_malloc.o",
+                          "$COMMON/pb_common_with_malloc.o",
+                          "$COMMON/malloc_wrappers.o"])
+dec = malloc_env.Program(["decode_alltypes_pointer.c",
+                          "alltypes.pb.c",
+                          "$COMMON/pb_decode_with_malloc.o",
+                          "$COMMON/pb_common_with_malloc.o",
+                          "$COMMON/malloc_wrappers.o"])
+
+# Encode and compare results to non-pointer alltypes test case
+env.RunTest(enc)
+env.Compare(["encode_alltypes_pointer.output", "$BUILD/alltypes/encode_alltypes.output"])
+
+# Decode (under valgrind if available)
+valgrind = env.WhereIs('valgrind')
+kwargs = {}
+if valgrind:
+    kwargs['COMMAND'] = valgrind
+    kwargs['ARGS'] = ["-q", "--error-exitcode=99", dec[0].abspath]
+
+env.RunTest("decode_alltypes.output", [dec, "encode_alltypes_pointer.output"], **kwargs)
+
+# Do the same thing with the optional fields present
+env.RunTest("optionals.output", enc, ARGS = ['1'])
+env.Compare(["optionals.output", "$BUILD/alltypes/optionals.output"])
+
+kwargs['ARGS'] = kwargs.get('ARGS', []) + ['1']
+env.RunTest("optionals.decout", [dec, "optionals.output"], **kwargs)
+
diff --git a/third_party/nanopb/tests/alltypes_pointer/alltypes.options b/third_party/nanopb/tests/alltypes_pointer/alltypes.options
new file mode 100644
index 0000000..52abeb7
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes_pointer/alltypes.options
@@ -0,0 +1,3 @@
+# Generate all fields as pointers.
+* type:FT_POINTER
+
diff --git a/third_party/nanopb/tests/alltypes_pointer/decode_alltypes_pointer.c b/third_party/nanopb/tests/alltypes_pointer/decode_alltypes_pointer.c
new file mode 100644
index 0000000..1dbb6c5
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes_pointer/decode_alltypes_pointer.c
@@ -0,0 +1,180 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pb_decode.h>
+#include "alltypes.pb.h"
+#include "test_helpers.h"
+
+#define TEST(x) if (!(x)) { \
+    fprintf(stderr, "Test " #x " failed.\n"); \
+    status = false; \
+    }
+
+/* This function is called once from main(), it handles
+   the decoding and checks the fields. */
+bool check_alltypes(pb_istream_t *stream, int mode)
+{
+    bool status = true;
+    AllTypes alltypes;
+    
+    /* Fill with garbage to better detect initialization errors */
+    memset(&alltypes, 0xAA, sizeof(alltypes));
+    alltypes.extensions = 0;
+    
+    if (!pb_decode(stream, AllTypes_fields, &alltypes))
+        return false;
+    
+    TEST(alltypes.req_int32     && *alltypes.req_int32         == -1001);
+    TEST(alltypes.req_int64     && *alltypes.req_int64         == -1002);
+    TEST(alltypes.req_uint32    && *alltypes.req_uint32        == 1003);
+    TEST(alltypes.req_uint64    && *alltypes.req_uint64        == 1004);
+    TEST(alltypes.req_sint32    && *alltypes.req_sint32        == -1005);
+    TEST(alltypes.req_sint64    && *alltypes.req_sint64        == -1006);
+    TEST(alltypes.req_bool      && *alltypes.req_bool          == true);
+    
+    TEST(alltypes.req_fixed32   && *alltypes.req_fixed32       == 1008);
+    TEST(alltypes.req_sfixed32  && *alltypes.req_sfixed32      == -1009);
+    TEST(alltypes.req_float     && *alltypes.req_float         == 1010.0f);
+    
+    TEST(alltypes.req_fixed64   && *alltypes.req_fixed64       == 1011);
+    TEST(alltypes.req_sfixed64  && *alltypes.req_sfixed64      == -1012);
+    TEST(alltypes.req_double    && *alltypes.req_double        == 1013.0f);
+    
+    TEST(alltypes.req_string    && strcmp(alltypes.req_string, "1014") == 0);
+    TEST(alltypes.req_bytes     && alltypes.req_bytes->size == 4);
+    TEST(alltypes.req_bytes     && memcmp(&alltypes.req_bytes->bytes, "1015", 4) == 0);
+    TEST(alltypes.req_submsg    && alltypes.req_submsg->substuff1
+                                && strcmp(alltypes.req_submsg->substuff1, "1016") == 0);
+    TEST(alltypes.req_submsg    && alltypes.req_submsg->substuff2
+                                && *alltypes.req_submsg->substuff2 == 1016);
+    TEST(*alltypes.req_enum == MyEnum_Truth);
+
+    TEST(alltypes.rep_int32_count == 5 && alltypes.rep_int32[4] == -2001 && alltypes.rep_int32[0] == 0);
+    TEST(alltypes.rep_int64_count == 5 && alltypes.rep_int64[4] == -2002 && alltypes.rep_int64[0] == 0);
+    TEST(alltypes.rep_uint32_count == 5 && alltypes.rep_uint32[4] == 2003 && alltypes.rep_uint32[0] == 0);
+    TEST(alltypes.rep_uint64_count == 5 && alltypes.rep_uint64[4] == 2004 && alltypes.rep_uint64[0] == 0);
+    TEST(alltypes.rep_sint32_count == 5 && alltypes.rep_sint32[4] == -2005 && alltypes.rep_sint32[0] == 0);
+    TEST(alltypes.rep_sint64_count == 5 && alltypes.rep_sint64[4] == -2006 && alltypes.rep_sint64[0] == 0);
+    TEST(alltypes.rep_bool_count == 5 && alltypes.rep_bool[4] == true && alltypes.rep_bool[0] == false);
+    
+    TEST(alltypes.rep_fixed32_count == 5 && alltypes.rep_fixed32[4] == 2008 && alltypes.rep_fixed32[0] == 0);
+    TEST(alltypes.rep_sfixed32_count == 5 && alltypes.rep_sfixed32[4] == -2009 && alltypes.rep_sfixed32[0] == 0);
+    TEST(alltypes.rep_float_count == 5 && alltypes.rep_float[4] == 2010.0f && alltypes.rep_float[0] == 0.0f);
+    
+    TEST(alltypes.rep_fixed64_count == 5 && alltypes.rep_fixed64[4] == 2011 && alltypes.rep_fixed64[0] == 0);
+    TEST(alltypes.rep_sfixed64_count == 5 && alltypes.rep_sfixed64[4] == -2012 && alltypes.rep_sfixed64[0] == 0);
+    TEST(alltypes.rep_double_count == 5 && alltypes.rep_double[4] == 2013.0 && alltypes.rep_double[0] == 0.0);
+    
+    TEST(alltypes.rep_string_count == 5 && strcmp(alltypes.rep_string[4], "2014") == 0 && alltypes.rep_string[0][0] == '\0');
+    TEST(alltypes.rep_bytes_count == 5 && alltypes.rep_bytes[4]->size == 4 && alltypes.rep_bytes[0]->size == 0);
+    TEST(memcmp(&alltypes.rep_bytes[4]->bytes, "2015", 4) == 0);
+
+    TEST(alltypes.rep_submsg_count == 5);
+    TEST(strcmp(alltypes.rep_submsg[4].substuff1, "2016") == 0 && alltypes.rep_submsg[0].substuff1[0] == '\0');
+    TEST(*alltypes.rep_submsg[4].substuff2 == 2016 && *alltypes.rep_submsg[0].substuff2 == 0);
+    TEST(*alltypes.rep_submsg[4].substuff3 == 2016 && alltypes.rep_submsg[0].substuff3 == NULL);
+    
+    TEST(alltypes.rep_enum_count == 5 && alltypes.rep_enum[4] == MyEnum_Truth && alltypes.rep_enum[0] == MyEnum_Zero);
+    TEST(alltypes.rep_emptymsg_count == 5);
+
+    if (mode == 0)
+    {
+        /* Expect that optional values are not present */
+        TEST(alltypes.opt_int32         == NULL);
+        TEST(alltypes.opt_int64         == NULL);
+        TEST(alltypes.opt_uint32        == NULL);
+        TEST(alltypes.opt_uint64        == NULL);
+        TEST(alltypes.opt_sint32        == NULL);
+        TEST(alltypes.opt_sint64        == NULL);
+        TEST(alltypes.opt_bool          == NULL);
+        
+        TEST(alltypes.opt_fixed32       == NULL);
+        TEST(alltypes.opt_sfixed32      == NULL);
+        TEST(alltypes.opt_float         == NULL);
+        TEST(alltypes.opt_fixed64       == NULL);
+        TEST(alltypes.opt_sfixed64      == NULL);
+        TEST(alltypes.opt_double        == NULL);
+        
+        TEST(alltypes.opt_string        == NULL);
+        TEST(alltypes.opt_bytes         == NULL);
+        TEST(alltypes.opt_submsg        == NULL);
+        TEST(alltypes.opt_enum          == NULL);
+
+        TEST(alltypes.which_oneof       == 0);
+    }
+    else
+    {
+        /* Expect filled-in values */
+        TEST(alltypes.opt_int32 && *alltypes.opt_int32      == 3041);
+        TEST(alltypes.opt_int64 && *alltypes.opt_int64      == 3042);
+        TEST(alltypes.opt_uint32 && *alltypes.opt_uint32    == 3043);
+        TEST(alltypes.opt_uint64 && *alltypes.opt_uint64    == 3044);
+        TEST(alltypes.opt_sint32 && *alltypes.opt_sint32    == 3045);
+        TEST(alltypes.opt_sint64 && *alltypes.opt_sint64    == 3046);
+        TEST(alltypes.opt_bool && *alltypes.opt_bool        == true);
+        
+        TEST(alltypes.opt_fixed32 && *alltypes.opt_fixed32  == 3048);
+        TEST(alltypes.opt_sfixed32 && *alltypes.opt_sfixed32== 3049);
+        TEST(alltypes.opt_float && *alltypes.opt_float      == 3050.0f);
+        TEST(alltypes.opt_fixed64 && *alltypes.opt_fixed64  == 3051);
+        TEST(alltypes.opt_sfixed64 && *alltypes.opt_sfixed64== 3052);
+        TEST(alltypes.opt_double && *alltypes.opt_double    == 3053.0);
+        
+        TEST(alltypes.opt_string && strcmp(alltypes.opt_string, "3054") == 0);
+        TEST(alltypes.opt_bytes && alltypes.opt_bytes->size == 4);
+        TEST(alltypes.opt_bytes && memcmp(&alltypes.opt_bytes->bytes, "3055", 4) == 0);
+        TEST(alltypes.opt_submsg && strcmp(alltypes.opt_submsg->substuff1, "3056") == 0);
+        TEST(alltypes.opt_submsg && *alltypes.opt_submsg->substuff2 == 3056);
+        TEST(alltypes.opt_enum && *alltypes.opt_enum == MyEnum_Truth);
+        TEST(alltypes.opt_emptymsg);
+
+        TEST(alltypes.which_oneof == AllTypes_oneof_msg1_tag);
+        TEST(alltypes.oneof.oneof_msg1 && strcmp(alltypes.oneof.oneof_msg1->substuff1, "4059") == 0);
+        TEST(alltypes.oneof.oneof_msg1->substuff2 && *alltypes.oneof.oneof_msg1->substuff2 == 4059);
+    }
+    
+    TEST(alltypes.req_limits->int32_min && *alltypes.req_limits->int32_min   == INT32_MIN);
+    TEST(alltypes.req_limits->int32_max && *alltypes.req_limits->int32_max   == INT32_MAX);
+    TEST(alltypes.req_limits->uint32_min && *alltypes.req_limits->uint32_min == 0);
+    TEST(alltypes.req_limits->uint32_max && *alltypes.req_limits->uint32_max == UINT32_MAX);
+    TEST(alltypes.req_limits->int64_min && *alltypes.req_limits->int64_min   == INT64_MIN);
+    TEST(alltypes.req_limits->int64_max && *alltypes.req_limits->int64_max   == INT64_MAX);
+    TEST(alltypes.req_limits->uint64_min && *alltypes.req_limits->uint64_min == 0);
+    TEST(alltypes.req_limits->uint64_max && *alltypes.req_limits->uint64_max == UINT64_MAX);
+    TEST(alltypes.req_limits->enum_min && *alltypes.req_limits->enum_min     == HugeEnum_Negative);
+    TEST(alltypes.req_limits->enum_max && *alltypes.req_limits->enum_max     == HugeEnum_Positive);
+    
+    TEST(alltypes.end && *alltypes.end == 1099);
+
+    pb_release(AllTypes_fields, &alltypes);
+
+    return status;
+}
+
+int main(int argc, char **argv)
+{
+    uint8_t buffer[1024];
+    size_t count;
+    pb_istream_t stream;
+    
+    /* Whether to expect the optional values or the default values. */
+    int mode = (argc > 1) ? atoi(argv[1]) : 0;
+    
+    /* Read the data into buffer */
+    SET_BINARY_MODE(stdin);
+    count = fread(buffer, 1, sizeof(buffer), stdin);
+    
+    /* Construct a pb_istream_t for reading from the buffer */
+    stream = pb_istream_from_buffer(buffer, count);
+    
+    /* Decode and verify the message */
+    if (!check_alltypes(&stream, mode))
+    {
+        fprintf(stderr, "Test failed: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    }
+    else
+    {
+        return 0;
+    }
+}
diff --git a/third_party/nanopb/tests/alltypes_pointer/encode_alltypes_pointer.c b/third_party/nanopb/tests/alltypes_pointer/encode_alltypes_pointer.c
new file mode 100644
index 0000000..7b52662
--- /dev/null
+++ b/third_party/nanopb/tests/alltypes_pointer/encode_alltypes_pointer.c
@@ -0,0 +1,194 @@
+/* Attempts to test all the datatypes supported by ProtoBuf.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_encode.h>
+#include "alltypes.pb.h"
+#include "test_helpers.h"
+
+int main(int argc, char **argv)
+{
+    int mode = (argc > 1) ? atoi(argv[1]) : 0;
+    
+    /* Values for required fields */
+    int32_t     req_int32         = -1001;
+    int64_t     req_int64         = -1002;
+    uint32_t    req_uint32        = 1003;
+    uint64_t    req_uint64        = 1004;
+    int32_t     req_sint32        = -1005;
+    int64_t     req_sint64        = -1006;
+    bool        req_bool          = true;
+    uint32_t    req_fixed32       = 1008;
+    int32_t     req_sfixed32      = -1009;
+    float       req_float         = 1010.0f;
+    uint64_t    req_fixed64       = 1011;
+    int64_t     req_sfixed64      = -1012;
+    double      req_double        = 1013.0;
+    char*       req_string        = "1014";
+    PB_BYTES_ARRAY_T(4) req_bytes = {4, {'1', '0', '1', '5'}};
+    static int32_t req_substuff   = 1016;
+    SubMessage  req_submsg        = {"1016", &req_substuff};
+    MyEnum      req_enum          = MyEnum_Truth;
+    EmptyMessage req_emptymsg     = {0};
+    
+    int32_t     end               = 1099;
+
+    /* Values for repeated fields */
+    int32_t     rep_int32[5]      = {0, 0, 0, 0, -2001};
+    int64_t     rep_int64[5]      = {0, 0, 0, 0, -2002};
+    uint32_t    rep_uint32[5]     = {0, 0, 0, 0, 2003};
+    uint64_t    rep_uint64[5]     = {0, 0, 0, 0, 2004};
+    int32_t     rep_sint32[5]     = {0, 0, 0, 0, -2005};
+    int64_t     rep_sint64[5]     = {0, 0, 0, 0, -2006};
+    bool        rep_bool[5]       = {false, false, false, false, true};
+    uint32_t    rep_fixed32[5]    = {0, 0, 0, 0, 2008};
+    int32_t     rep_sfixed32[5]   = {0, 0, 0, 0, -2009};
+    float       rep_float[5]      = {0, 0, 0, 0, 2010.0f};
+    uint64_t    rep_fixed64[5]    = {0, 0, 0, 0, 2011};
+    int64_t     rep_sfixed64[5]   = {0, 0, 0, 0, -2012};
+    double      rep_double[5]     = {0, 0, 0, 0, 2013.0f};
+    char*       rep_string[5]     = {"", "", "", "", "2014"};
+    static PB_BYTES_ARRAY_T(4) rep_bytes_4 = {4, {'2', '0', '1', '5'}};
+    pb_bytes_array_t *rep_bytes[5]= {NULL, NULL, NULL, NULL, (pb_bytes_array_t*)&rep_bytes_4};
+    static int32_t rep_sub2zero   = 0;
+    static int32_t rep_substuff2  = 2016;
+    static uint32_t rep_substuff3 = 2016;
+    SubMessage  rep_submsg[5]     = {{"", &rep_sub2zero},
+                                     {"", &rep_sub2zero},
+                                     {"", &rep_sub2zero},
+                                     {"", &rep_sub2zero},
+                                     {"2016", &rep_substuff2, &rep_substuff3}};
+    MyEnum      rep_enum[5]       = {0, 0, 0, 0, MyEnum_Truth};
+    EmptyMessage rep_emptymsg[5]  = {{0}, {0}, {0}, {0}, {0}};
+
+    /* Values for optional fields */
+    int32_t     opt_int32         = 3041;
+    int64_t     opt_int64         = 3042;
+    uint32_t    opt_uint32        = 3043;
+    uint64_t    opt_uint64        = 3044;
+    int32_t     opt_sint32        = 3045;
+    int64_t     opt_sint64        = 3046;
+    bool        opt_bool          = true;
+    uint32_t    opt_fixed32       = 3048;
+    int32_t     opt_sfixed32      = 3049;
+    float       opt_float         = 3050.0f;
+    uint64_t    opt_fixed64       = 3051;
+    int64_t     opt_sfixed64      = 3052;
+    double      opt_double        = 3053.0;
+    char*       opt_string        = "3054";
+    PB_BYTES_ARRAY_T(4) opt_bytes = {4, {'3', '0', '5', '5'}};
+    static int32_t opt_substuff   = 3056;
+    SubMessage  opt_submsg        = {"3056", &opt_substuff};
+    MyEnum      opt_enum          = MyEnum_Truth;
+    EmptyMessage opt_emptymsg     = {0};
+
+    static int32_t oneof_substuff = 4059;
+    SubMessage  oneof_msg1        = {"4059", &oneof_substuff};
+
+    /* Values for the Limits message. */
+    static int32_t  int32_min  = INT32_MIN;
+    static int32_t  int32_max  = INT32_MAX;
+    static uint32_t uint32_min = 0;
+    static uint32_t uint32_max = UINT32_MAX;
+    static int64_t  int64_min  = INT64_MIN;
+    static int64_t  int64_max  = INT64_MAX;
+    static uint64_t uint64_min = 0;
+    static uint64_t uint64_max = UINT64_MAX;
+    static HugeEnum enum_min   = HugeEnum_Negative;
+    static HugeEnum enum_max   = HugeEnum_Positive;
+    Limits req_limits = {&int32_min,    &int32_max,
+                         &uint32_min,   &uint32_max,
+                         &int64_min,    &int64_max,
+                         &uint64_min,   &uint64_max,
+                         &enum_min,     &enum_max};
+
+    /* Initialize the message struct with pointers to the fields. */
+    AllTypes alltypes = {0};
+
+    alltypes.req_int32         = &req_int32;
+    alltypes.req_int64         = &req_int64;
+    alltypes.req_uint32        = &req_uint32;
+    alltypes.req_uint64        = &req_uint64;
+    alltypes.req_sint32        = &req_sint32;
+    alltypes.req_sint64        = &req_sint64;
+    alltypes.req_bool          = &req_bool;
+    alltypes.req_fixed32       = &req_fixed32;
+    alltypes.req_sfixed32      = &req_sfixed32;
+    alltypes.req_float         = &req_float;
+    alltypes.req_fixed64       = &req_fixed64;
+    alltypes.req_sfixed64      = &req_sfixed64;
+    alltypes.req_double        = &req_double;
+    alltypes.req_string        = req_string;
+    alltypes.req_bytes         = (pb_bytes_array_t*)&req_bytes;
+    alltypes.req_submsg        = &req_submsg;
+    alltypes.req_enum          = &req_enum;
+    alltypes.req_emptymsg      = &req_emptymsg;
+    alltypes.req_limits        = &req_limits;
+    
+    alltypes.rep_int32_count    = 5; alltypes.rep_int32     = rep_int32;
+    alltypes.rep_int64_count    = 5; alltypes.rep_int64     = rep_int64;
+    alltypes.rep_uint32_count   = 5; alltypes.rep_uint32    = rep_uint32;
+    alltypes.rep_uint64_count   = 5; alltypes.rep_uint64    = rep_uint64;
+    alltypes.rep_sint32_count   = 5; alltypes.rep_sint32    = rep_sint32;
+    alltypes.rep_sint64_count   = 5; alltypes.rep_sint64    = rep_sint64;
+    alltypes.rep_bool_count     = 5; alltypes.rep_bool      = rep_bool;
+    alltypes.rep_fixed32_count  = 5; alltypes.rep_fixed32   = rep_fixed32;
+    alltypes.rep_sfixed32_count = 5; alltypes.rep_sfixed32  = rep_sfixed32;
+    alltypes.rep_float_count    = 5; alltypes.rep_float     = rep_float;
+    alltypes.rep_fixed64_count  = 5; alltypes.rep_fixed64   = rep_fixed64;
+    alltypes.rep_sfixed64_count = 5; alltypes.rep_sfixed64  = rep_sfixed64;
+    alltypes.rep_double_count   = 5; alltypes.rep_double    = rep_double;
+    alltypes.rep_string_count   = 5; alltypes.rep_string    = rep_string;
+    alltypes.rep_bytes_count    = 5; alltypes.rep_bytes     = rep_bytes;
+    alltypes.rep_submsg_count   = 5; alltypes.rep_submsg    = rep_submsg;
+    alltypes.rep_enum_count     = 5; alltypes.rep_enum      = rep_enum;
+    alltypes.rep_emptymsg_count = 5; alltypes.rep_emptymsg  = rep_emptymsg;
+    
+    if (mode != 0)
+    {
+        /* Fill in values for optional fields */
+        alltypes.opt_int32         = &opt_int32;
+        alltypes.opt_int64         = &opt_int64;
+        alltypes.opt_uint32        = &opt_uint32;
+        alltypes.opt_uint64        = &opt_uint64;
+        alltypes.opt_sint32        = &opt_sint32;
+        alltypes.opt_sint64        = &opt_sint64;
+        alltypes.opt_bool          = &opt_bool;
+        alltypes.opt_fixed32       = &opt_fixed32;
+        alltypes.opt_sfixed32      = &opt_sfixed32;
+        alltypes.opt_float         = &opt_float;
+        alltypes.opt_fixed64       = &opt_fixed64;
+        alltypes.opt_sfixed64      = &opt_sfixed64;
+        alltypes.opt_double        = &opt_double;
+        alltypes.opt_string        = opt_string;
+        alltypes.opt_bytes         = (pb_bytes_array_t*)&opt_bytes;
+        alltypes.opt_submsg        = &opt_submsg;
+        alltypes.opt_enum          = &opt_enum;
+        alltypes.opt_emptymsg      = &opt_emptymsg;
+
+        alltypes.which_oneof = AllTypes_oneof_msg1_tag;
+        alltypes.oneof.oneof_msg1 = &oneof_msg1;
+    }
+    
+    alltypes.end = &end;
+    
+    {
+        uint8_t buffer[4096];
+        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+        
+        /* Now encode it and check if we succeeded. */
+        if (pb_encode(&stream, AllTypes_fields, &alltypes))
+        {
+            SET_BINARY_MODE(stdout);
+            fwrite(buffer, 1, stream.bytes_written, stdout);
+            return 0; /* Success */
+        }
+        else
+        {
+            fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
+            return 1; /* Failure */
+        }
+    }
+}
diff --git a/third_party/nanopb/tests/anonymous_oneof/SConscript b/third_party/nanopb/tests/anonymous_oneof/SConscript
new file mode 100644
index 0000000..1067228
--- /dev/null
+++ b/third_party/nanopb/tests/anonymous_oneof/SConscript
@@ -0,0 +1,30 @@
+# Test anonymous_oneof generator option
+
+Import('env')
+
+import re
+
+match = None
+if 'PROTOC_VERSION' in env:
+    match = re.search('([0-9]+).([0-9]+).([0-9]+)', env['PROTOC_VERSION'])
+
+if match:
+    version = map(int, match.groups())
+
+# Oneof is supported by protoc >= 2.6.0
+if env.GetOption('clean') or (match and (version[0] > 2 or (version[0] == 2 and version[1] >= 6))):
+    # Anonymous oneofs are supported by clang and gcc
+    if 'clang' in env['CC'] or 'gcc' in env['CC']:
+        env2 = env.Clone()
+        if '-pedantic' in env2['CFLAGS']:
+            env2['CFLAGS'].remove('-pedantic')
+        env2.NanopbProto('oneof')
+
+        dec = env2.Program(['decode_oneof.c',
+                            'oneof.pb.c',
+                            '$COMMON/pb_decode.o',
+                            '$COMMON/pb_common.o'])
+
+        env2.RunTest("message1.txt", [dec, '$BUILD/oneof/message1.pb'], ARGS = ['1'])
+        env2.RunTest("message2.txt", [dec, '$BUILD/oneof/message2.pb'], ARGS = ['2'])
+        env2.RunTest("message3.txt", [dec, '$BUILD/oneof/message3.pb'], ARGS = ['3'])
diff --git a/third_party/nanopb/tests/anonymous_oneof/decode_oneof.c b/third_party/nanopb/tests/anonymous_oneof/decode_oneof.c
new file mode 100644
index 0000000..0f774db
--- /dev/null
+++ b/third_party/nanopb/tests/anonymous_oneof/decode_oneof.c
@@ -0,0 +1,88 @@
+/* Decode a message using oneof fields */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_decode.h>
+#include "oneof.pb.h"
+#include "test_helpers.h"
+#include "unittests.h"
+
+/* Test the 'AnonymousOneOfMessage' */
+int test_oneof_1(pb_istream_t *stream, int option)
+{
+    AnonymousOneOfMessage msg;
+    int status = 0;
+
+    /* To better catch initialization errors */
+    memset(&msg, 0xAA, sizeof(msg));
+
+    if (!pb_decode(stream, AnonymousOneOfMessage_fields, &msg))
+    {
+        printf("Decoding failed: %s\n", PB_GET_ERROR(stream));
+        return 1;
+    }
+
+    /* Check that the basic fields work normally */
+    TEST(msg.prefix == 123);
+    TEST(msg.suffix == 321);
+
+    /* Check that we got the right oneof according to command line */
+    if (option == 1)
+    {
+        TEST(msg.which_values == AnonymousOneOfMessage_first_tag);
+        TEST(msg.first == 999);
+    }
+    else if (option == 2)
+    {
+        TEST(msg.which_values == AnonymousOneOfMessage_second_tag);
+        TEST(strcmp(msg.second, "abcd") == 0);
+    }
+    else if (option == 3)
+    {
+        TEST(msg.which_values == AnonymousOneOfMessage_third_tag);
+        TEST(msg.third.array[0] == 1);
+        TEST(msg.third.array[1] == 2);
+        TEST(msg.third.array[2] == 3);
+        TEST(msg.third.array[3] == 4);
+        TEST(msg.third.array[4] == 5);
+    }
+
+    return status;
+}
+
+int main(int argc, char **argv)
+{
+    uint8_t buffer[AnonymousOneOfMessage_size];
+    size_t count;
+    int option;
+
+    if (argc != 2)
+    {
+        fprintf(stderr, "Usage: decode_oneof [number]\n");
+        return 1;
+    }
+    option = atoi(argv[1]);
+
+    SET_BINARY_MODE(stdin);
+    count = fread(buffer, 1, sizeof(buffer), stdin);
+
+    if (!feof(stdin))
+    {
+        printf("Message does not fit in buffer\n");
+        return 1;
+    }
+
+    {
+        int status = 0;
+        pb_istream_t stream;
+
+        stream = pb_istream_from_buffer(buffer, count);
+        status = test_oneof_1(&stream, option);
+
+        if (status != 0)
+            return status;
+    }
+
+    return 0;
+}
diff --git a/third_party/nanopb/tests/anonymous_oneof/oneof.proto b/third_party/nanopb/tests/anonymous_oneof/oneof.proto
new file mode 100644
index 0000000..d56285c
--- /dev/null
+++ b/third_party/nanopb/tests/anonymous_oneof/oneof.proto
@@ -0,0 +1,23 @@
+syntax = "proto2";
+
+import 'nanopb.proto';
+
+message SubMessage
+{
+    repeated int32 array = 1 [(nanopb).max_count = 8];
+}
+
+/* Oneof in a message with other fields */
+message AnonymousOneOfMessage
+{
+    option (nanopb_msgopt).anonymous_oneof = true;
+    required int32 prefix = 1;
+    oneof values
+    {
+        int32 first = 5;
+        string second = 6 [(nanopb).max_size = 8];
+        SubMessage third = 7;
+    }
+    required int32 suffix = 99;
+}
+
diff --git a/third_party/nanopb/tests/backwards_compatibility/SConscript b/third_party/nanopb/tests/backwards_compatibility/SConscript
new file mode 100644
index 0000000..81b0318
--- /dev/null
+++ b/third_party/nanopb/tests/backwards_compatibility/SConscript
@@ -0,0 +1,11 @@
+# Check that the old generated .pb.c/.pb.h files are still compatible with the
+# current version of nanopb.
+
+Import("env")
+
+enc = env.Program(["encode_legacy.c", "alltypes_legacy.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"])
+dec = env.Program(["decode_legacy.c", "alltypes_legacy.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"])
+
+env.RunTest(enc)
+env.RunTest([dec, "encode_legacy.output"])
+
diff --git a/third_party/nanopb/tests/backwards_compatibility/alltypes_legacy.c b/third_party/nanopb/tests/backwards_compatibility/alltypes_legacy.c
new file mode 100644
index 0000000..7311fd4
--- /dev/null
+++ b/third_party/nanopb/tests/backwards_compatibility/alltypes_legacy.c
@@ -0,0 +1,153 @@
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.0-dev at Tue Aug 19 17:53:24 2014. */
+
+#include "alltypes_legacy.h"
+
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+const char SubMessage_substuff1_default[16] = "1";
+const int32_t SubMessage_substuff2_default = 2;
+const uint32_t SubMessage_substuff3_default = 3u;
+const int32_t Limits_int32_min_default = 2147483647;
+const int32_t Limits_int32_max_default = -2147483647;
+const uint32_t Limits_uint32_min_default = 4294967295u;
+const uint32_t Limits_uint32_max_default = 0u;
+const int64_t Limits_int64_min_default = 9223372036854775807ll;
+const int64_t Limits_int64_max_default = -9223372036854775807ll;
+const uint64_t Limits_uint64_min_default = 18446744073709551615ull;
+const uint64_t Limits_uint64_max_default = 0ull;
+const HugeEnum Limits_enum_min_default = HugeEnum_Positive;
+const HugeEnum Limits_enum_max_default = HugeEnum_Negative;
+const int32_t AllTypes_opt_int32_default = 4041;
+const int64_t AllTypes_opt_int64_default = 4042ll;
+const uint32_t AllTypes_opt_uint32_default = 4043u;
+const uint64_t AllTypes_opt_uint64_default = 4044ull;
+const int32_t AllTypes_opt_sint32_default = 4045;
+const int64_t AllTypes_opt_sint64_default = 4046;
+const bool AllTypes_opt_bool_default = false;
+const uint32_t AllTypes_opt_fixed32_default = 4048u;
+const int32_t AllTypes_opt_sfixed32_default = 4049;
+const float AllTypes_opt_float_default = 4050;
+const uint64_t AllTypes_opt_fixed64_default = 4051ull;
+const int64_t AllTypes_opt_sfixed64_default = 4052ll;
+const double AllTypes_opt_double_default = 4053;
+const char AllTypes_opt_string_default[16] = "4054";
+const AllTypes_opt_bytes_t AllTypes_opt_bytes_default = {4, {0x34,0x30,0x35,0x35}};
+const MyEnum AllTypes_opt_enum_default = MyEnum_Second;
+
+
+const pb_field_t SubMessage_fields[4] = {
+    PB_FIELD(  1, STRING  , REQUIRED, STATIC  , FIRST, SubMessage, substuff1, substuff1, &SubMessage_substuff1_default),
+    PB_FIELD(  2, INT32   , REQUIRED, STATIC  , OTHER, SubMessage, substuff2, substuff1, &SubMessage_substuff2_default),
+    PB_FIELD(  3, FIXED32 , OPTIONAL, STATIC  , OTHER, SubMessage, substuff3, substuff2, &SubMessage_substuff3_default),
+    PB_LAST_FIELD
+};
+
+const pb_field_t EmptyMessage_fields[1] = {
+    PB_LAST_FIELD
+};
+
+const pb_field_t Limits_fields[11] = {
+    PB_FIELD(  1, INT32   , REQUIRED, STATIC  , FIRST, Limits, int32_min, int32_min, &Limits_int32_min_default),
+    PB_FIELD(  2, INT32   , REQUIRED, STATIC  , OTHER, Limits, int32_max, int32_min, &Limits_int32_max_default),
+    PB_FIELD(  3, UINT32  , REQUIRED, STATIC  , OTHER, Limits, uint32_min, int32_max, &Limits_uint32_min_default),
+    PB_FIELD(  4, UINT32  , REQUIRED, STATIC  , OTHER, Limits, uint32_max, uint32_min, &Limits_uint32_max_default),
+    PB_FIELD(  5, INT64   , REQUIRED, STATIC  , OTHER, Limits, int64_min, uint32_max, &Limits_int64_min_default),
+    PB_FIELD(  6, INT64   , REQUIRED, STATIC  , OTHER, Limits, int64_max, int64_min, &Limits_int64_max_default),
+    PB_FIELD(  7, UINT64  , REQUIRED, STATIC  , OTHER, Limits, uint64_min, int64_max, &Limits_uint64_min_default),
+    PB_FIELD(  8, UINT64  , REQUIRED, STATIC  , OTHER, Limits, uint64_max, uint64_min, &Limits_uint64_max_default),
+    PB_FIELD(  9, ENUM    , REQUIRED, STATIC  , OTHER, Limits, enum_min, uint64_max, &Limits_enum_min_default),
+    PB_FIELD( 10, ENUM    , REQUIRED, STATIC  , OTHER, Limits, enum_max, enum_min, &Limits_enum_max_default),
+    PB_LAST_FIELD
+};
+
+const pb_field_t AllTypes_fields[54] = {
+    PB_FIELD(  1, INT32   , REQUIRED, STATIC  , FIRST, AllTypes, req_int32, req_int32, 0),
+    PB_FIELD(  2, INT64   , REQUIRED, STATIC  , OTHER, AllTypes, req_int64, req_int32, 0),
+    PB_FIELD(  3, UINT32  , REQUIRED, STATIC  , OTHER, AllTypes, req_uint32, req_int64, 0),
+    PB_FIELD(  4, UINT64  , REQUIRED, STATIC  , OTHER, AllTypes, req_uint64, req_uint32, 0),
+    PB_FIELD(  5, SINT32  , REQUIRED, STATIC  , OTHER, AllTypes, req_sint32, req_uint64, 0),
+    PB_FIELD(  6, SINT64  , REQUIRED, STATIC  , OTHER, AllTypes, req_sint64, req_sint32, 0),
+    PB_FIELD(  7, BOOL    , REQUIRED, STATIC  , OTHER, AllTypes, req_bool, req_sint64, 0),
+    PB_FIELD(  8, FIXED32 , REQUIRED, STATIC  , OTHER, AllTypes, req_fixed32, req_bool, 0),
+    PB_FIELD(  9, SFIXED32, REQUIRED, STATIC  , OTHER, AllTypes, req_sfixed32, req_fixed32, 0),
+    PB_FIELD( 10, FLOAT   , REQUIRED, STATIC  , OTHER, AllTypes, req_float, req_sfixed32, 0),
+    PB_FIELD( 11, FIXED64 , REQUIRED, STATIC  , OTHER, AllTypes, req_fixed64, req_float, 0),
+    PB_FIELD( 12, SFIXED64, REQUIRED, STATIC  , OTHER, AllTypes, req_sfixed64, req_fixed64, 0),
+    PB_FIELD( 13, DOUBLE  , REQUIRED, STATIC  , OTHER, AllTypes, req_double, req_sfixed64, 0),
+    PB_FIELD( 14, STRING  , REQUIRED, STATIC  , OTHER, AllTypes, req_string, req_double, 0),
+    PB_FIELD( 15, BYTES   , REQUIRED, STATIC  , OTHER, AllTypes, req_bytes, req_string, 0),
+    PB_FIELD( 16, MESSAGE , REQUIRED, STATIC  , OTHER, AllTypes, req_submsg, req_bytes, &SubMessage_fields),
+    PB_FIELD( 17, ENUM    , REQUIRED, STATIC  , OTHER, AllTypes, req_enum, req_submsg, 0),
+    PB_FIELD( 21, INT32   , REPEATED, STATIC  , OTHER, AllTypes, rep_int32, req_enum, 0),
+    PB_FIELD( 22, INT64   , REPEATED, STATIC  , OTHER, AllTypes, rep_int64, rep_int32, 0),
+    PB_FIELD( 23, UINT32  , REPEATED, STATIC  , OTHER, AllTypes, rep_uint32, rep_int64, 0),
+    PB_FIELD( 24, UINT64  , REPEATED, STATIC  , OTHER, AllTypes, rep_uint64, rep_uint32, 0),
+    PB_FIELD( 25, SINT32  , REPEATED, STATIC  , OTHER, AllTypes, rep_sint32, rep_uint64, 0),
+    PB_FIELD( 26, SINT64  , REPEATED, STATIC  , OTHER, AllTypes, rep_sint64, rep_sint32, 0),
+    PB_FIELD( 27, BOOL    , REPEATED, STATIC  , OTHER, AllTypes, rep_bool, rep_sint64, 0),
+    PB_FIELD( 28, FIXED32 , REPEATED, STATIC  , OTHER, AllTypes, rep_fixed32, rep_bool, 0),
+    PB_FIELD( 29, SFIXED32, REPEATED, STATIC  , OTHER, AllTypes, rep_sfixed32, rep_fixed32, 0),
+    PB_FIELD( 30, FLOAT   , REPEATED, STATIC  , OTHER, AllTypes, rep_float, rep_sfixed32, 0),
+    PB_FIELD( 31, FIXED64 , REPEATED, STATIC  , OTHER, AllTypes, rep_fixed64, rep_float, 0),
+    PB_FIELD( 32, SFIXED64, REPEATED, STATIC  , OTHER, AllTypes, rep_sfixed64, rep_fixed64, 0),
+    PB_FIELD( 33, DOUBLE  , REPEATED, STATIC  , OTHER, AllTypes, rep_double, rep_sfixed64, 0),
+    PB_FIELD( 34, STRING  , REPEATED, STATIC  , OTHER, AllTypes, rep_string, rep_double, 0),
+    PB_FIELD( 35, BYTES   , REPEATED, STATIC  , OTHER, AllTypes, rep_bytes, rep_string, 0),
+    PB_FIELD( 36, MESSAGE , REPEATED, STATIC  , OTHER, AllTypes, rep_submsg, rep_bytes, &SubMessage_fields),
+    PB_FIELD( 37, ENUM    , REPEATED, STATIC  , OTHER, AllTypes, rep_enum, rep_submsg, 0),
+    PB_FIELD( 41, INT32   , OPTIONAL, STATIC  , OTHER, AllTypes, opt_int32, rep_enum, &AllTypes_opt_int32_default),
+    PB_FIELD( 42, INT64   , OPTIONAL, STATIC  , OTHER, AllTypes, opt_int64, opt_int32, &AllTypes_opt_int64_default),
+    PB_FIELD( 43, UINT32  , OPTIONAL, STATIC  , OTHER, AllTypes, opt_uint32, opt_int64, &AllTypes_opt_uint32_default),
+    PB_FIELD( 44, UINT64  , OPTIONAL, STATIC  , OTHER, AllTypes, opt_uint64, opt_uint32, &AllTypes_opt_uint64_default),
+    PB_FIELD( 45, SINT32  , OPTIONAL, STATIC  , OTHER, AllTypes, opt_sint32, opt_uint64, &AllTypes_opt_sint32_default),
+    PB_FIELD( 46, SINT64  , OPTIONAL, STATIC  , OTHER, AllTypes, opt_sint64, opt_sint32, &AllTypes_opt_sint64_default),
+    PB_FIELD( 47, BOOL    , OPTIONAL, STATIC  , OTHER, AllTypes, opt_bool, opt_sint64, &AllTypes_opt_bool_default),
+    PB_FIELD( 48, FIXED32 , OPTIONAL, STATIC  , OTHER, AllTypes, opt_fixed32, opt_bool, &AllTypes_opt_fixed32_default),
+    PB_FIELD( 49, SFIXED32, OPTIONAL, STATIC  , OTHER, AllTypes, opt_sfixed32, opt_fixed32, &AllTypes_opt_sfixed32_default),
+    PB_FIELD( 50, FLOAT   , OPTIONAL, STATIC  , OTHER, AllTypes, opt_float, opt_sfixed32, &AllTypes_opt_float_default),
+    PB_FIELD( 51, FIXED64 , OPTIONAL, STATIC  , OTHER, AllTypes, opt_fixed64, opt_float, &AllTypes_opt_fixed64_default),
+    PB_FIELD( 52, SFIXED64, OPTIONAL, STATIC  , OTHER, AllTypes, opt_sfixed64, opt_fixed64, &AllTypes_opt_sfixed64_default),
+    PB_FIELD( 53, DOUBLE  , OPTIONAL, STATIC  , OTHER, AllTypes, opt_double, opt_sfixed64, &AllTypes_opt_double_default),
+    PB_FIELD( 54, STRING  , OPTIONAL, STATIC  , OTHER, AllTypes, opt_string, opt_double, &AllTypes_opt_string_default),
+    PB_FIELD( 55, BYTES   , OPTIONAL, STATIC  , OTHER, AllTypes, opt_bytes, opt_string, &AllTypes_opt_bytes_default),
+    PB_FIELD( 56, MESSAGE , OPTIONAL, STATIC  , OTHER, AllTypes, opt_submsg, opt_bytes, &SubMessage_fields),
+    PB_FIELD( 57, ENUM    , OPTIONAL, STATIC  , OTHER, AllTypes, opt_enum, opt_submsg, &AllTypes_opt_enum_default),
+    PB_FIELD( 99, INT32   , REQUIRED, STATIC  , OTHER, AllTypes, end, opt_enum, 0),
+    PB_FIELD(200, EXTENSION, OPTIONAL, CALLBACK, OTHER, AllTypes, extensions, end, 0),
+    PB_LAST_FIELD
+};
+
+
+/* Check that field information fits in pb_field_t */
+#if !defined(PB_FIELD_32BIT)
+/* If you get an error here, it means that you need to define PB_FIELD_32BIT
+ * compile-time option. You can do that in pb.h or on compiler command line.
+ * 
+ * The reason you need to do this is that some of your messages contain tag
+ * numbers or field sizes that are larger than what can fit in 8 or 16 bit
+ * field descriptors.
+ */
+PB_STATIC_ASSERT((pb_membersize(AllTypes, req_submsg) < 65536 && pb_membersize(AllTypes, rep_submsg[0]) < 65536 && pb_membersize(AllTypes, opt_submsg) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_SubMessage_EmptyMessage_Limits_AllTypes)
+#endif
+
+#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
+/* If you get an error here, it means that you need to define PB_FIELD_16BIT
+ * compile-time option. You can do that in pb.h or on compiler command line.
+ * 
+ * The reason you need to do this is that some of your messages contain tag
+ * numbers or field sizes that are larger than what can fit in the default
+ * 8 bit descriptors.
+ */
+PB_STATIC_ASSERT((pb_membersize(AllTypes, req_submsg) < 256 && pb_membersize(AllTypes, rep_submsg[0]) < 256 && pb_membersize(AllTypes, opt_submsg) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_SubMessage_EmptyMessage_Limits_AllTypes)
+#endif
+
+
+/* On some platforms (such as AVR), double is really float.
+ * These are not directly supported by nanopb, but see example_avr_double.
+ * To get rid of this error, remove any double fields from your .proto.
+ */
+PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)
+
diff --git a/third_party/nanopb/tests/backwards_compatibility/alltypes_legacy.h b/third_party/nanopb/tests/backwards_compatibility/alltypes_legacy.h
new file mode 100644
index 0000000..4e0a63b
--- /dev/null
+++ b/third_party/nanopb/tests/backwards_compatibility/alltypes_legacy.h
@@ -0,0 +1,274 @@
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.0-dev at Tue Aug 19 17:53:24 2014. */
+
+#ifndef PB_ALLTYPES_LEGACY_H_INCLUDED
+#define PB_ALLTYPES_LEGACY_H_INCLUDED
+#include <pb.h>
+
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Enum definitions */
+typedef enum _HugeEnum {
+    HugeEnum_Negative = -2147483647,
+    HugeEnum_Positive = 2147483647
+} HugeEnum;
+
+typedef enum _MyEnum {
+    MyEnum_Zero = 0,
+    MyEnum_First = 1,
+    MyEnum_Second = 2,
+    MyEnum_Truth = 42
+} MyEnum;
+
+/* Struct definitions */
+typedef struct _EmptyMessage {
+    uint8_t dummy_field;
+} EmptyMessage;
+
+typedef struct _Limits {
+    int32_t int32_min;
+    int32_t int32_max;
+    uint32_t uint32_min;
+    uint32_t uint32_max;
+    int64_t int64_min;
+    int64_t int64_max;
+    uint64_t uint64_min;
+    uint64_t uint64_max;
+    HugeEnum enum_min;
+    HugeEnum enum_max;
+} Limits;
+
+typedef struct _SubMessage {
+    char substuff1[16];
+    int32_t substuff2;
+    bool has_substuff3;
+    uint32_t substuff3;
+} SubMessage;
+
+typedef PB_BYTES_ARRAY_T(16) AllTypes_req_bytes_t;
+
+typedef PB_BYTES_ARRAY_T(16) AllTypes_rep_bytes_t;
+
+typedef PB_BYTES_ARRAY_T(16) AllTypes_opt_bytes_t;
+
+typedef struct _AllTypes {
+    int32_t req_int32;
+    int64_t req_int64;
+    uint32_t req_uint32;
+    uint64_t req_uint64;
+    int32_t req_sint32;
+    int64_t req_sint64;
+    bool req_bool;
+    uint32_t req_fixed32;
+    int32_t req_sfixed32;
+    float req_float;
+    uint64_t req_fixed64;
+    int64_t req_sfixed64;
+    double req_double;
+    char req_string[16];
+    AllTypes_req_bytes_t req_bytes;
+    SubMessage req_submsg;
+    MyEnum req_enum;
+    pb_size_t rep_int32_count;
+    int32_t rep_int32[5];
+    pb_size_t rep_int64_count;
+    int64_t rep_int64[5];
+    pb_size_t rep_uint32_count;
+    uint32_t rep_uint32[5];
+    pb_size_t rep_uint64_count;
+    uint64_t rep_uint64[5];
+    pb_size_t rep_sint32_count;
+    int32_t rep_sint32[5];
+    pb_size_t rep_sint64_count;
+    int64_t rep_sint64[5];
+    pb_size_t rep_bool_count;
+    bool rep_bool[5];
+    pb_size_t rep_fixed32_count;
+    uint32_t rep_fixed32[5];
+    pb_size_t rep_sfixed32_count;
+    int32_t rep_sfixed32[5];
+    pb_size_t rep_float_count;
+    float rep_float[5];
+    pb_size_t rep_fixed64_count;
+    uint64_t rep_fixed64[5];
+    pb_size_t rep_sfixed64_count;
+    int64_t rep_sfixed64[5];
+    pb_size_t rep_double_count;
+    double rep_double[5];
+    pb_size_t rep_string_count;
+    char rep_string[5][16];
+    pb_size_t rep_bytes_count;
+    AllTypes_rep_bytes_t rep_bytes[5];
+    pb_size_t rep_submsg_count;
+    SubMessage rep_submsg[5];
+    pb_size_t rep_enum_count;
+    MyEnum rep_enum[5];
+    bool has_opt_int32;
+    int32_t opt_int32;
+    bool has_opt_int64;
+    int64_t opt_int64;
+    bool has_opt_uint32;
+    uint32_t opt_uint32;
+    bool has_opt_uint64;
+    uint64_t opt_uint64;
+    bool has_opt_sint32;
+    int32_t opt_sint32;
+    bool has_opt_sint64;
+    int64_t opt_sint64;
+    bool has_opt_bool;
+    bool opt_bool;
+    bool has_opt_fixed32;
+    uint32_t opt_fixed32;
+    bool has_opt_sfixed32;
+    int32_t opt_sfixed32;
+    bool has_opt_float;
+    float opt_float;
+    bool has_opt_fixed64;
+    uint64_t opt_fixed64;
+    bool has_opt_sfixed64;
+    int64_t opt_sfixed64;
+    bool has_opt_double;
+    double opt_double;
+    bool has_opt_string;
+    char opt_string[16];
+    bool has_opt_bytes;
+    AllTypes_opt_bytes_t opt_bytes;
+    bool has_opt_submsg;
+    SubMessage opt_submsg;
+    bool has_opt_enum;
+    MyEnum opt_enum;
+    int32_t end;
+    pb_extension_t *extensions;
+} AllTypes;
+
+/* Default values for struct fields */
+extern const char SubMessage_substuff1_default[16];
+extern const int32_t SubMessage_substuff2_default;
+extern const uint32_t SubMessage_substuff3_default;
+extern const int32_t Limits_int32_min_default;
+extern const int32_t Limits_int32_max_default;
+extern const uint32_t Limits_uint32_min_default;
+extern const uint32_t Limits_uint32_max_default;
+extern const int64_t Limits_int64_min_default;
+extern const int64_t Limits_int64_max_default;
+extern const uint64_t Limits_uint64_min_default;
+extern const uint64_t Limits_uint64_max_default;
+extern const HugeEnum Limits_enum_min_default;
+extern const HugeEnum Limits_enum_max_default;
+extern const int32_t AllTypes_opt_int32_default;
+extern const int64_t AllTypes_opt_int64_default;
+extern const uint32_t AllTypes_opt_uint32_default;
+extern const uint64_t AllTypes_opt_uint64_default;
+extern const int32_t AllTypes_opt_sint32_default;
+extern const int64_t AllTypes_opt_sint64_default;
+extern const bool AllTypes_opt_bool_default;
+extern const uint32_t AllTypes_opt_fixed32_default;
+extern const int32_t AllTypes_opt_sfixed32_default;
+extern const float AllTypes_opt_float_default;
+extern const uint64_t AllTypes_opt_fixed64_default;
+extern const int64_t AllTypes_opt_sfixed64_default;
+extern const double AllTypes_opt_double_default;
+extern const char AllTypes_opt_string_default[16];
+extern const AllTypes_opt_bytes_t AllTypes_opt_bytes_default;
+extern const MyEnum AllTypes_opt_enum_default;
+
+/* Initializer values for message structs */
+#define SubMessage_init_default                  {"1", 2, false, 3u}
+#define EmptyMessage_init_default                {0}
+#define Limits_init_default                      {2147483647, -2147483647, 4294967295u, 0u, 9223372036854775807ll, -9223372036854775807ll, 18446744073709551615ull, 0ull, HugeEnum_Positive, HugeEnum_Negative}
+#define AllTypes_init_default                    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", {0, {0}}, SubMessage_init_default, (MyEnum)0, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {"", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}, 0, {SubMessage_init_default, SubMessage_init_default, SubMessage_init_default, SubMessage_init_default, SubMessage_init_default}, 0, {(MyEnum)0, (MyEnum)0, (MyEnum)0, (MyEnum)0, (MyEnum)0}, false, 4041, false, 4042ll, false, 4043u, false, 4044ull, false, 4045, false, 4046, false, false, false, 4048u, false, 4049, false, 4050, false, 4051ull, false, 4052ll, false, 4053, false, "4054", false, {4, {0x34,0x30,0x35,0x35}}, false, SubMessage_init_default, false, MyEnum_Second, 0, NULL}
+#define SubMessage_init_zero                     {"", 0, false, 0}
+#define EmptyMessage_init_zero                   {0}
+#define Limits_init_zero                         {0, 0, 0, 0, 0, 0, 0, 0, (HugeEnum)0, (HugeEnum)0}
+#define AllTypes_init_zero                       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", {0, {0}}, SubMessage_init_zero, (MyEnum)0, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {"", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}, 0, {SubMessage_init_zero, SubMessage_init_zero, SubMessage_init_zero, SubMessage_init_zero, SubMessage_init_zero}, 0, {(MyEnum)0, (MyEnum)0, (MyEnum)0, (MyEnum)0, (MyEnum)0}, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, "", false, {0, {0}}, false, SubMessage_init_zero, false, (MyEnum)0, 0, NULL}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define Limits_int32_min_tag                     1
+#define Limits_int32_max_tag                     2
+#define Limits_uint32_min_tag                    3
+#define Limits_uint32_max_tag                    4
+#define Limits_int64_min_tag                     5
+#define Limits_int64_max_tag                     6
+#define Limits_uint64_min_tag                    7
+#define Limits_uint64_max_tag                    8
+#define Limits_enum_min_tag                      9
+#define Limits_enum_max_tag                      10
+#define SubMessage_substuff1_tag                 1
+#define SubMessage_substuff2_tag                 2
+#define SubMessage_substuff3_tag                 3
+#define AllTypes_req_int32_tag                   1
+#define AllTypes_req_int64_tag                   2
+#define AllTypes_req_uint32_tag                  3
+#define AllTypes_req_uint64_tag                  4
+#define AllTypes_req_sint32_tag                  5
+#define AllTypes_req_sint64_tag                  6
+#define AllTypes_req_bool_tag                    7
+#define AllTypes_req_fixed32_tag                 8
+#define AllTypes_req_sfixed32_tag                9
+#define AllTypes_req_float_tag                   10
+#define AllTypes_req_fixed64_tag                 11
+#define AllTypes_req_sfixed64_tag                12
+#define AllTypes_req_double_tag                  13
+#define AllTypes_req_string_tag                  14
+#define AllTypes_req_bytes_tag                   15
+#define AllTypes_req_submsg_tag                  16
+#define AllTypes_req_enum_tag                    17
+#define AllTypes_rep_int32_tag                   21
+#define AllTypes_rep_int64_tag                   22
+#define AllTypes_rep_uint32_tag                  23
+#define AllTypes_rep_uint64_tag                  24
+#define AllTypes_rep_sint32_tag                  25
+#define AllTypes_rep_sint64_tag                  26
+#define AllTypes_rep_bool_tag                    27
+#define AllTypes_rep_fixed32_tag                 28
+#define AllTypes_rep_sfixed32_tag                29
+#define AllTypes_rep_float_tag                   30
+#define AllTypes_rep_fixed64_tag                 31
+#define AllTypes_rep_sfixed64_tag                32
+#define AllTypes_rep_double_tag                  33
+#define AllTypes_rep_string_tag                  34
+#define AllTypes_rep_bytes_tag                   35
+#define AllTypes_rep_submsg_tag                  36
+#define AllTypes_rep_enum_tag                    37
+#define AllTypes_opt_int32_tag                   41
+#define AllTypes_opt_int64_tag                   42
+#define AllTypes_opt_uint32_tag                  43
+#define AllTypes_opt_uint64_tag                  44
+#define AllTypes_opt_sint32_tag                  45
+#define AllTypes_opt_sint64_tag                  46
+#define AllTypes_opt_bool_tag                    47
+#define AllTypes_opt_fixed32_tag                 48
+#define AllTypes_opt_sfixed32_tag                49
+#define AllTypes_opt_float_tag                   50
+#define AllTypes_opt_fixed64_tag                 51
+#define AllTypes_opt_sfixed64_tag                52
+#define AllTypes_opt_double_tag                  53
+#define AllTypes_opt_string_tag                  54
+#define AllTypes_opt_bytes_tag                   55
+#define AllTypes_opt_submsg_tag                  56
+#define AllTypes_opt_enum_tag                    57
+#define AllTypes_end_tag                         99
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t SubMessage_fields[4];
+extern const pb_field_t EmptyMessage_fields[1];
+extern const pb_field_t Limits_fields[11];
+extern const pb_field_t AllTypes_fields[54];
+
+/* Maximum encoded size of messages (where known) */
+#define SubMessage_size                          34
+#define EmptyMessage_size                        0
+#define Limits_size                              90
+#define AllTypes_size                            1362
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/third_party/nanopb/tests/backwards_compatibility/alltypes_legacy.options b/third_party/nanopb/tests/backwards_compatibility/alltypes_legacy.options
new file mode 100644
index 0000000..b31e3cf
--- /dev/null
+++ b/third_party/nanopb/tests/backwards_compatibility/alltypes_legacy.options
@@ -0,0 +1,3 @@
+* max_size:16
+* max_count:5
+
diff --git a/third_party/nanopb/tests/backwards_compatibility/alltypes_legacy.proto b/third_party/nanopb/tests/backwards_compatibility/alltypes_legacy.proto
new file mode 100644
index 0000000..f5bc35c
--- /dev/null
+++ b/third_party/nanopb/tests/backwards_compatibility/alltypes_legacy.proto
@@ -0,0 +1,110 @@
+syntax = "proto2";
+
+message SubMessage {
+    required string substuff1 = 1 [default = "1"];
+    required int32 substuff2 = 2 [default = 2];
+    optional fixed32 substuff3 = 3 [default = 3];
+}
+
+message EmptyMessage {
+
+}
+
+enum HugeEnum {
+    Negative = -2147483647; /* protoc doesn't accept -2147483648 here */
+    Positive =  2147483647;
+}
+
+message Limits {
+    required int32      int32_min  =  1 [default = 2147483647];
+    required int32      int32_max  =  2 [default = -2147483647];
+    required uint32     uint32_min =  3 [default = 4294967295];
+    required uint32     uint32_max =  4 [default = 0];
+    required int64      int64_min  =  5 [default = 9223372036854775807];
+    required int64      int64_max  =  6 [default = -9223372036854775807];
+    required uint64     uint64_min =  7 [default = 18446744073709551615];
+    required uint64     uint64_max =  8 [default = 0];
+    required HugeEnum   enum_min   =  9 [default = Positive];
+    required HugeEnum   enum_max   = 10 [default = Negative];
+}
+
+enum MyEnum {
+    Zero = 0;
+    First = 1;
+    Second = 2;
+    Truth = 42;
+}
+
+message AllTypes {
+    required int32      req_int32   = 1;
+    required int64      req_int64   = 2;
+    required uint32     req_uint32  = 3;
+    required uint64     req_uint64  = 4;
+    required sint32     req_sint32  = 5;
+    required sint64     req_sint64  = 6;
+    required bool       req_bool    = 7;
+    
+    required fixed32    req_fixed32 = 8;
+    required sfixed32   req_sfixed32= 9;
+    required float      req_float   = 10;
+    
+    required fixed64    req_fixed64 = 11;
+    required sfixed64   req_sfixed64= 12;
+    required double     req_double  = 13;
+    
+    required string     req_string  = 14;
+    required bytes      req_bytes   = 15;
+    required SubMessage req_submsg  = 16;
+    required MyEnum     req_enum    = 17;
+    
+    
+    repeated int32      rep_int32   = 21 [packed = true];
+    repeated int64      rep_int64   = 22 [packed = true];
+    repeated uint32     rep_uint32  = 23 [packed = true];
+    repeated uint64     rep_uint64  = 24 [packed = true];
+    repeated sint32     rep_sint32  = 25 [packed = true];
+    repeated sint64     rep_sint64  = 26 [packed = true];
+    repeated bool       rep_bool    = 27 [packed = true];
+    
+    repeated fixed32    rep_fixed32 = 28 [packed = true];
+    repeated sfixed32   rep_sfixed32= 29 [packed = true];
+    repeated float      rep_float   = 30 [packed = true];
+    
+    repeated fixed64    rep_fixed64 = 31 [packed = true];
+    repeated sfixed64   rep_sfixed64= 32 [packed = true];
+    repeated double     rep_double  = 33 [packed = true];
+    
+    repeated string     rep_string  = 34;
+    repeated bytes      rep_bytes   = 35;
+    repeated SubMessage rep_submsg  = 36;
+    repeated MyEnum     rep_enum    = 37 [packed = true];
+    
+    optional int32      opt_int32   = 41 [default = 4041];
+    optional int64      opt_int64   = 42 [default = 4042];
+    optional uint32     opt_uint32  = 43 [default = 4043];
+    optional uint64     opt_uint64  = 44 [default = 4044];
+    optional sint32     opt_sint32  = 45 [default = 4045];
+    optional sint64     opt_sint64  = 46 [default = 4046];
+    optional bool       opt_bool    = 47 [default = false];
+    
+    optional fixed32    opt_fixed32 = 48 [default = 4048];
+    optional sfixed32   opt_sfixed32= 49 [default = 4049];
+    optional float      opt_float   = 50 [default = 4050];
+    
+    optional fixed64    opt_fixed64 = 51 [default = 4051];
+    optional sfixed64   opt_sfixed64= 52 [default = 4052];
+    optional double     opt_double  = 53 [default = 4053];
+    
+    optional string     opt_string  = 54 [default = "4054"];
+    optional bytes      opt_bytes   = 55 [default = "4055"];
+    optional SubMessage opt_submsg  = 56;
+    optional MyEnum     opt_enum    = 57 [default = Second];
+
+    // Just to make sure that the size of the fields has been calculated
+    // properly, i.e. otherwise a bug in last field might not be detected.
+    required int32      end = 99;
+
+
+    extensions 200 to 255;
+}
+
diff --git a/third_party/nanopb/tests/backwards_compatibility/decode_legacy.c b/third_party/nanopb/tests/backwards_compatibility/decode_legacy.c
new file mode 100644
index 0000000..5f5b6bb
--- /dev/null
+++ b/third_party/nanopb/tests/backwards_compatibility/decode_legacy.c
@@ -0,0 +1,199 @@
+/* Tests the decoding of all types.
+ * This is a backwards-compatibility test, using alltypes_legacy.h.
+ * It is similar to decode_alltypes, but duplicated in order to allow
+ * decode_alltypes to test any new features introduced later.
+ *
+ * Run e.g. ./encode_legacy | ./decode_legacy
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pb_decode.h>
+#include "alltypes_legacy.h"
+#include "test_helpers.h"
+
+#define TEST(x) if (!(x)) { \
+    printf("Test " #x " failed.\n"); \
+    return false; \
+    }
+
+/* This function is called once from main(), it handles
+   the decoding and checks the fields. */
+bool check_alltypes(pb_istream_t *stream, int mode)
+{
+    AllTypes alltypes = {0};
+    
+    if (!pb_decode(stream, AllTypes_fields, &alltypes))
+        return false;
+    
+    TEST(alltypes.req_int32         == -1001);
+    TEST(alltypes.req_int64         == -1002);
+    TEST(alltypes.req_uint32        == 1003);
+    TEST(alltypes.req_uint64        == 1004);
+    TEST(alltypes.req_sint32        == -1005);
+    TEST(alltypes.req_sint64        == -1006);
+    TEST(alltypes.req_bool          == true);
+    
+    TEST(alltypes.req_fixed32       == 1008);
+    TEST(alltypes.req_sfixed32      == -1009);
+    TEST(alltypes.req_float         == 1010.0f);
+    
+    TEST(alltypes.req_fixed64       == 1011);
+    TEST(alltypes.req_sfixed64      == -1012);
+    TEST(alltypes.req_double        == 1013.0f);
+    
+    TEST(strcmp(alltypes.req_string, "1014") == 0);
+    TEST(alltypes.req_bytes.size == 4);
+    TEST(memcmp(alltypes.req_bytes.bytes, "1015", 4) == 0);
+    TEST(strcmp(alltypes.req_submsg.substuff1, "1016") == 0);
+    TEST(alltypes.req_submsg.substuff2 == 1016);
+    TEST(alltypes.req_submsg.substuff3 == 3);
+    TEST(alltypes.req_enum == MyEnum_Truth);
+    
+    TEST(alltypes.rep_int32_count == 5 && alltypes.rep_int32[4] == -2001 && alltypes.rep_int32[0] == 0);
+    TEST(alltypes.rep_int64_count == 5 && alltypes.rep_int64[4] == -2002 && alltypes.rep_int64[0] == 0);
+    TEST(alltypes.rep_uint32_count == 5 && alltypes.rep_uint32[4] == 2003 && alltypes.rep_uint32[0] == 0);
+    TEST(alltypes.rep_uint64_count == 5 && alltypes.rep_uint64[4] == 2004 && alltypes.rep_uint64[0] == 0);
+    TEST(alltypes.rep_sint32_count == 5 && alltypes.rep_sint32[4] == -2005 && alltypes.rep_sint32[0] == 0);
+    TEST(alltypes.rep_sint64_count == 5 && alltypes.rep_sint64[4] == -2006 && alltypes.rep_sint64[0] == 0);
+    TEST(alltypes.rep_bool_count == 5 && alltypes.rep_bool[4] == true && alltypes.rep_bool[0] == false);
+    
+    TEST(alltypes.rep_fixed32_count == 5 && alltypes.rep_fixed32[4] == 2008 && alltypes.rep_fixed32[0] == 0);
+    TEST(alltypes.rep_sfixed32_count == 5 && alltypes.rep_sfixed32[4] == -2009 && alltypes.rep_sfixed32[0] == 0);
+    TEST(alltypes.rep_float_count == 5 && alltypes.rep_float[4] == 2010.0f && alltypes.rep_float[0] == 0.0f);
+    
+    TEST(alltypes.rep_fixed64_count == 5 && alltypes.rep_fixed64[4] == 2011 && alltypes.rep_fixed64[0] == 0);
+    TEST(alltypes.rep_sfixed64_count == 5 && alltypes.rep_sfixed64[4] == -2012 && alltypes.rep_sfixed64[0] == 0);
+    TEST(alltypes.rep_double_count == 5 && alltypes.rep_double[4] == 2013.0 && alltypes.rep_double[0] == 0.0);
+    
+    TEST(alltypes.rep_string_count == 5 && strcmp(alltypes.rep_string[4], "2014") == 0 && alltypes.rep_string[0][0] == '\0');
+    TEST(alltypes.rep_bytes_count == 5 && alltypes.rep_bytes[4].size == 4 && alltypes.rep_bytes[0].size == 0);
+    TEST(memcmp(alltypes.rep_bytes[4].bytes, "2015", 4) == 0);
+
+    TEST(alltypes.rep_submsg_count == 5);
+    TEST(strcmp(alltypes.rep_submsg[4].substuff1, "2016") == 0 && alltypes.rep_submsg[0].substuff1[0] == '\0');
+    TEST(alltypes.rep_submsg[4].substuff2 == 2016 && alltypes.rep_submsg[0].substuff2 == 0);
+    TEST(alltypes.rep_submsg[4].substuff3 == 2016 && alltypes.rep_submsg[0].substuff3 == 3);
+    
+    TEST(alltypes.rep_enum_count == 5 && alltypes.rep_enum[4] == MyEnum_Truth && alltypes.rep_enum[0] == MyEnum_Zero);
+    
+    if (mode == 0)
+    {
+        /* Expect default values */
+        TEST(alltypes.has_opt_int32     == false);
+        TEST(alltypes.opt_int32         == 4041);
+        TEST(alltypes.has_opt_int64     == false);
+        TEST(alltypes.opt_int64         == 4042);
+        TEST(alltypes.has_opt_uint32    == false);
+        TEST(alltypes.opt_uint32        == 4043);
+        TEST(alltypes.has_opt_uint64    == false);
+        TEST(alltypes.opt_uint64        == 4044);
+        TEST(alltypes.has_opt_sint32    == false);
+        TEST(alltypes.opt_sint32        == 4045);
+        TEST(alltypes.has_opt_sint64    == false);
+        TEST(alltypes.opt_sint64        == 4046);
+        TEST(alltypes.has_opt_bool      == false);
+        TEST(alltypes.opt_bool          == false);
+        
+        TEST(alltypes.has_opt_fixed32   == false);
+        TEST(alltypes.opt_fixed32       == 4048);
+        TEST(alltypes.has_opt_sfixed32  == false);
+        TEST(alltypes.opt_sfixed32      == 4049);
+        TEST(alltypes.has_opt_float     == false);
+        TEST(alltypes.opt_float         == 4050.0f);
+        
+        TEST(alltypes.has_opt_fixed64   == false);
+        TEST(alltypes.opt_fixed64       == 4051);
+        TEST(alltypes.has_opt_sfixed64  == false);
+        TEST(alltypes.opt_sfixed64      == 4052);
+        TEST(alltypes.has_opt_double    == false);
+        TEST(alltypes.opt_double        == 4053.0);
+        
+        TEST(alltypes.has_opt_string    == false);
+        TEST(strcmp(alltypes.opt_string, "4054") == 0);
+        TEST(alltypes.has_opt_bytes     == false);
+        TEST(alltypes.opt_bytes.size == 4);
+        TEST(memcmp(alltypes.opt_bytes.bytes, "4055", 4) == 0);
+        TEST(alltypes.has_opt_submsg    == false);
+        TEST(strcmp(alltypes.opt_submsg.substuff1, "1") == 0);
+        TEST(alltypes.opt_submsg.substuff2 == 2);
+        TEST(alltypes.opt_submsg.substuff3 == 3);
+        TEST(alltypes.has_opt_enum     == false);
+        TEST(alltypes.opt_enum == MyEnum_Second);
+    }
+    else
+    {
+        /* Expect filled-in values */
+        TEST(alltypes.has_opt_int32     == true);
+        TEST(alltypes.opt_int32         == 3041);
+        TEST(alltypes.has_opt_int64     == true);
+        TEST(alltypes.opt_int64         == 3042);
+        TEST(alltypes.has_opt_uint32    == true);
+        TEST(alltypes.opt_uint32        == 3043);
+        TEST(alltypes.has_opt_uint64    == true);
+        TEST(alltypes.opt_uint64        == 3044);
+        TEST(alltypes.has_opt_sint32    == true);
+        TEST(alltypes.opt_sint32        == 3045);
+        TEST(alltypes.has_opt_sint64    == true);
+        TEST(alltypes.opt_sint64        == 3046);
+        TEST(alltypes.has_opt_bool      == true);
+        TEST(alltypes.opt_bool          == true);
+        
+        TEST(alltypes.has_opt_fixed32   == true);
+        TEST(alltypes.opt_fixed32       == 3048);
+        TEST(alltypes.has_opt_sfixed32  == true);
+        TEST(alltypes.opt_sfixed32      == 3049);
+        TEST(alltypes.has_opt_float     == true);
+        TEST(alltypes.opt_float         == 3050.0f);
+        
+        TEST(alltypes.has_opt_fixed64   == true);
+        TEST(alltypes.opt_fixed64       == 3051);
+        TEST(alltypes.has_opt_sfixed64  == true);
+        TEST(alltypes.opt_sfixed64      == 3052);
+        TEST(alltypes.has_opt_double    == true);
+        TEST(alltypes.opt_double        == 3053.0);
+        
+        TEST(alltypes.has_opt_string    == true);
+        TEST(strcmp(alltypes.opt_string, "3054") == 0);
+        TEST(alltypes.has_opt_bytes     == true);
+        TEST(alltypes.opt_bytes.size == 4);
+        TEST(memcmp(alltypes.opt_bytes.bytes, "3055", 4) == 0);
+        TEST(alltypes.has_opt_submsg    == true);
+        TEST(strcmp(alltypes.opt_submsg.substuff1, "3056") == 0);
+        TEST(alltypes.opt_submsg.substuff2 == 3056);
+        TEST(alltypes.opt_submsg.substuff3 == 3);
+        TEST(alltypes.has_opt_enum      == true);
+        TEST(alltypes.opt_enum == MyEnum_Truth);
+    }
+    
+    TEST(alltypes.end == 1099);
+    
+    return true;
+}
+
+int main(int argc, char **argv)
+{
+    uint8_t buffer[1024];
+    size_t count;
+    pb_istream_t stream;
+
+    /* Whether to expect the optional values or the default values. */
+    int mode = (argc > 1) ? atoi(argv[1]) : 0;
+    
+    /* Read the data into buffer */
+    SET_BINARY_MODE(stdin);
+    count = fread(buffer, 1, sizeof(buffer), stdin);
+    
+    /* Construct a pb_istream_t for reading from the buffer */
+    stream = pb_istream_from_buffer(buffer, count);
+    
+    /* Decode and print out the stuff */
+    if (!check_alltypes(&stream, mode))
+    {
+        printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    } else {
+        return 0;
+    }
+}
diff --git a/third_party/nanopb/tests/backwards_compatibility/encode_legacy.c b/third_party/nanopb/tests/backwards_compatibility/encode_legacy.c
new file mode 100644
index 0000000..5c9d41b
--- /dev/null
+++ b/third_party/nanopb/tests/backwards_compatibility/encode_legacy.c
@@ -0,0 +1,135 @@
+/* Attempts to test all the datatypes supported by ProtoBuf.
+ * This is a backwards-compatibility test, using alltypes_legacy.h.
+ * It is similar to encode_alltypes, but duplicated in order to allow
+ * encode_alltypes to test any new features introduced later.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_encode.h>
+#include "alltypes_legacy.h"
+#include "test_helpers.h"
+
+int main(int argc, char **argv)
+{
+    int mode = (argc > 1) ? atoi(argv[1]) : 0;
+    
+    /* Initialize the structure with constants */
+    AllTypes alltypes = {0};
+    
+    alltypes.req_int32         = -1001;
+    alltypes.req_int64         = -1002;
+    alltypes.req_uint32        = 1003;
+    alltypes.req_uint64        = 1004;
+    alltypes.req_sint32        = -1005;
+    alltypes.req_sint64        = -1006;
+    alltypes.req_bool          = true;
+    
+    alltypes.req_fixed32       = 1008;
+    alltypes.req_sfixed32      = -1009;
+    alltypes.req_float         = 1010.0f;
+    
+    alltypes.req_fixed64       = 1011;
+    alltypes.req_sfixed64      = -1012;
+    alltypes.req_double        = 1013.0;
+    
+    strcpy(alltypes.req_string, "1014");
+    alltypes.req_bytes.size = 4;
+    memcpy(alltypes.req_bytes.bytes, "1015", 4);
+    strcpy(alltypes.req_submsg.substuff1, "1016");
+    alltypes.req_submsg.substuff2 = 1016;
+    alltypes.req_enum = MyEnum_Truth;
+    
+    alltypes.rep_int32_count = 5; alltypes.rep_int32[4] = -2001;
+    alltypes.rep_int64_count = 5; alltypes.rep_int64[4] = -2002;
+    alltypes.rep_uint32_count = 5; alltypes.rep_uint32[4] = 2003;
+    alltypes.rep_uint64_count = 5; alltypes.rep_uint64[4] = 2004;
+    alltypes.rep_sint32_count = 5; alltypes.rep_sint32[4] = -2005;
+    alltypes.rep_sint64_count = 5; alltypes.rep_sint64[4] = -2006;
+    alltypes.rep_bool_count = 5; alltypes.rep_bool[4] = true;
+    
+    alltypes.rep_fixed32_count = 5; alltypes.rep_fixed32[4] = 2008;
+    alltypes.rep_sfixed32_count = 5; alltypes.rep_sfixed32[4] = -2009;
+    alltypes.rep_float_count = 5; alltypes.rep_float[4] = 2010.0f;
+    
+    alltypes.rep_fixed64_count = 5; alltypes.rep_fixed64[4] = 2011;
+    alltypes.rep_sfixed64_count = 5; alltypes.rep_sfixed64[4] = -2012;
+    alltypes.rep_double_count = 5; alltypes.rep_double[4] = 2013.0;
+    
+    alltypes.rep_string_count = 5; strcpy(alltypes.rep_string[4], "2014");
+    alltypes.rep_bytes_count = 5; alltypes.rep_bytes[4].size = 4;
+    memcpy(alltypes.rep_bytes[4].bytes, "2015", 4);
+
+    alltypes.rep_submsg_count = 5;
+    strcpy(alltypes.rep_submsg[4].substuff1, "2016");
+    alltypes.rep_submsg[4].substuff2 = 2016;
+    alltypes.rep_submsg[4].has_substuff3 = true;
+    alltypes.rep_submsg[4].substuff3 = 2016;
+    
+    alltypes.rep_enum_count = 5; alltypes.rep_enum[4] = MyEnum_Truth;
+    
+    if (mode != 0)
+    {
+        /* Fill in values for optional fields */
+        alltypes.has_opt_int32 = true;
+        alltypes.opt_int32         = 3041;
+        alltypes.has_opt_int64 = true;
+        alltypes.opt_int64         = 3042;
+        alltypes.has_opt_uint32 = true;
+        alltypes.opt_uint32        = 3043;
+        alltypes.has_opt_uint64 = true;
+        alltypes.opt_uint64        = 3044;
+        alltypes.has_opt_sint32 = true;
+        alltypes.opt_sint32        = 3045;
+        alltypes.has_opt_sint64 = true;
+        alltypes.opt_sint64        = 3046;
+        alltypes.has_opt_bool = true;
+        alltypes.opt_bool          = true;
+        
+        alltypes.has_opt_fixed32 = true;
+        alltypes.opt_fixed32       = 3048;
+        alltypes.has_opt_sfixed32 = true;
+        alltypes.opt_sfixed32      = 3049;
+        alltypes.has_opt_float = true;
+        alltypes.opt_float         = 3050.0f;
+        
+        alltypes.has_opt_fixed64 = true;
+        alltypes.opt_fixed64       = 3051;
+        alltypes.has_opt_sfixed64 = true;
+        alltypes.opt_sfixed64      = 3052;
+        alltypes.has_opt_double = true;
+        alltypes.opt_double        = 3053.0;
+        
+        alltypes.has_opt_string = true;
+        strcpy(alltypes.opt_string, "3054");
+        alltypes.has_opt_bytes = true;
+        alltypes.opt_bytes.size = 4;
+        memcpy(alltypes.opt_bytes.bytes, "3055", 4);
+        alltypes.has_opt_submsg = true;
+        strcpy(alltypes.opt_submsg.substuff1, "3056");
+        alltypes.opt_submsg.substuff2 = 3056;
+        alltypes.has_opt_enum = true;
+        alltypes.opt_enum = MyEnum_Truth;
+    }
+    
+    alltypes.end = 1099;
+
+    {    
+        uint8_t buffer[1024];
+        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+        
+        /* Now encode it and check if we succeeded. */
+        if (pb_encode(&stream, AllTypes_fields, &alltypes))
+        {
+            SET_BINARY_MODE(stdout);
+            fwrite(buffer, 1, stream.bytes_written, stdout);
+            return 0; /* Success */
+        }
+        else
+        {
+            fprintf(stderr, "Encoding failed!\n");
+            return 1; /* Failure */
+        }
+    }
+}
diff --git a/third_party/nanopb/tests/basic_buffer/SConscript b/third_party/nanopb/tests/basic_buffer/SConscript
new file mode 100644
index 0000000..acaf5ff
--- /dev/null
+++ b/third_party/nanopb/tests/basic_buffer/SConscript
@@ -0,0 +1,12 @@
+# Build and run a basic round-trip test using memory buffer encoding.
+
+Import("env")
+
+enc = env.Program(["encode_buffer.c", "$COMMON/person.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"])
+dec = env.Program(["decode_buffer.c", "$COMMON/person.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"])
+
+env.RunTest(enc)
+env.RunTest([dec, "encode_buffer.output"])
+env.Decode(["encode_buffer.output", "$COMMON/person.proto"], MESSAGE = "Person")
+env.Compare(["decode_buffer.output", "encode_buffer.decoded"])
+
diff --git a/third_party/nanopb/tests/basic_buffer/decode_buffer.c b/third_party/nanopb/tests/basic_buffer/decode_buffer.c
new file mode 100644
index 0000000..291d164
--- /dev/null
+++ b/third_party/nanopb/tests/basic_buffer/decode_buffer.c
@@ -0,0 +1,88 @@
+/* A very simple decoding test case, using person.proto.
+ * Produces output compatible with protoc --decode.
+ * Reads the encoded data from stdin and prints the values
+ * to stdout as text.
+ *
+ * Run e.g. ./test_encode1 | ./test_decode1
+ */
+
+#include <stdio.h>
+#include <pb_decode.h>
+#include "person.pb.h"
+#include "test_helpers.h"
+
+/* This function is called once from main(), it handles
+   the decoding and printing. */
+bool print_person(pb_istream_t *stream)
+{
+    int i;
+    Person person = Person_init_zero;
+    
+    if (!pb_decode(stream, Person_fields, &person))
+        return false;
+    
+    /* Now the decoding is done, rest is just to print stuff out. */
+
+    printf("name: \"%s\"\n", person.name);
+    printf("id: %ld\n", (long)person.id);
+    
+    if (person.has_email)
+        printf("email: \"%s\"\n", person.email);
+    
+    for (i = 0; i < person.phone_count; i++)
+    {
+        Person_PhoneNumber *phone = &person.phone[i];
+        printf("phone {\n");
+        printf("  number: \"%s\"\n", phone->number);
+        
+        if (phone->has_type)
+        {
+            switch (phone->type)
+            {
+                case Person_PhoneType_WORK:
+                    printf("  type: WORK\n");
+                    break;
+                
+                case Person_PhoneType_HOME:
+                    printf("  type: HOME\n");
+                    break;
+                
+                case Person_PhoneType_MOBILE:
+                    printf("  type: MOBILE\n");
+                    break;
+            }
+        }
+        printf("}\n");
+    }
+    
+    return true;
+}
+
+int main()
+{
+    uint8_t buffer[Person_size];
+    pb_istream_t stream;
+    size_t count;
+    
+    /* Read the data into buffer */
+    SET_BINARY_MODE(stdin);
+    count = fread(buffer, 1, sizeof(buffer), stdin);
+    
+    if (!feof(stdin))
+    {
+    	printf("Message does not fit in buffer\n");
+    	return 1;
+    }
+    
+    /* Construct a pb_istream_t for reading from the buffer */
+    stream = pb_istream_from_buffer(buffer, count);
+    
+    /* Decode and print out the stuff */
+    if (!print_person(&stream))
+    {
+        printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    } else {
+        return 0;
+    }
+}
diff --git a/third_party/nanopb/tests/basic_buffer/encode_buffer.c b/third_party/nanopb/tests/basic_buffer/encode_buffer.c
new file mode 100644
index 0000000..c412c14
--- /dev/null
+++ b/third_party/nanopb/tests/basic_buffer/encode_buffer.c
@@ -0,0 +1,38 @@
+/* A very simple encoding test case using person.proto.
+ * Just puts constant data in the fields and encodes into
+ * buffer, which is then written to stdout.
+ */
+
+#include <stdio.h>
+#include <pb_encode.h>
+#include "person.pb.h"
+#include "test_helpers.h"
+
+int main()
+{
+    uint8_t buffer[Person_size];
+    pb_ostream_t stream;
+    
+    /* Initialize the structure with constants */
+    Person person = {"Test Person 99", 99, true, "test@person.com",
+        3, {{"555-12345678", true, Person_PhoneType_MOBILE},
+            {"99-2342", false, 0},
+            {"1234-5678", true, Person_PhoneType_WORK},
+        }};
+
+    stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+    
+    /* Now encode it and check if we succeeded. */
+    if (pb_encode(&stream, Person_fields, &person))
+    {    
+        /* Write the result data to stdout */
+        SET_BINARY_MODE(stdout);
+        fwrite(buffer, 1, stream.bytes_written, stdout);
+        return 0; /* Success */
+    }
+    else
+    {
+        fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
+        return 1; /* Failure */
+    }
+}
diff --git a/third_party/nanopb/tests/basic_stream/SConscript b/third_party/nanopb/tests/basic_stream/SConscript
new file mode 100644
index 0000000..7d66856
--- /dev/null
+++ b/third_party/nanopb/tests/basic_stream/SConscript
@@ -0,0 +1,12 @@
+# Build and run a basic round-trip test using direct stream encoding.
+
+Import("env")
+
+enc = env.Program(["encode_stream.c", "$COMMON/person.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"])
+dec = env.Program(["decode_stream.c", "$COMMON/person.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"])
+
+env.RunTest(enc)
+env.RunTest([dec, "encode_stream.output"])
+env.Decode(["encode_stream.output", "$COMMON/person.proto"], MESSAGE = "Person")
+env.Compare(["decode_stream.output", "encode_stream.decoded"])
+
diff --git a/third_party/nanopb/tests/basic_stream/decode_stream.c b/third_party/nanopb/tests/basic_stream/decode_stream.c
new file mode 100644
index 0000000..798dcc5
--- /dev/null
+++ b/third_party/nanopb/tests/basic_stream/decode_stream.c
@@ -0,0 +1,84 @@
+/* Same as test_decode1 but reads from stdin directly.
+ */
+
+#include <stdio.h>
+#include <pb_decode.h>
+#include "person.pb.h"
+#include "test_helpers.h"
+
+/* This function is called once from main(), it handles
+   the decoding and printing.
+   Ugly copy-paste from test_decode1.c. */
+bool print_person(pb_istream_t *stream)
+{
+    int i;
+    Person person = Person_init_zero;
+    
+    if (!pb_decode(stream, Person_fields, &person))
+        return false;
+    
+    /* Now the decoding is done, rest is just to print stuff out. */
+
+    printf("name: \"%s\"\n", person.name);
+    printf("id: %ld\n", (long)person.id);
+    
+    if (person.has_email)
+        printf("email: \"%s\"\n", person.email);
+    
+    for (i = 0; i < person.phone_count; i++)
+    {
+        Person_PhoneNumber *phone = &person.phone[i];
+        printf("phone {\n");
+        printf("  number: \"%s\"\n", phone->number);
+        
+        if (phone->has_type)
+        {
+            switch (phone->type)
+            {
+                case Person_PhoneType_WORK:
+                    printf("  type: WORK\n");
+                    break;
+                
+                case Person_PhoneType_HOME:
+                    printf("  type: HOME\n");
+                    break;
+                
+                case Person_PhoneType_MOBILE:
+                    printf("  type: MOBILE\n");
+                    break;
+            }
+        }
+        printf("}\n");
+    }
+    
+    return true;
+}
+
+/* This binds the pb_istream_t to stdin */
+bool callback(pb_istream_t *stream, uint8_t *buf, size_t count)
+{
+    FILE *file = (FILE*)stream->state;
+    bool status;
+    
+    status = (fread(buf, 1, count, file) == count);
+    
+    if (feof(file))
+        stream->bytes_left = 0;
+    
+    return status;
+}
+
+int main()
+{
+    pb_istream_t stream = {&callback, NULL, SIZE_MAX};
+    stream.state = stdin;
+    SET_BINARY_MODE(stdin);
+
+    if (!print_person(&stream))
+    {
+        printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    } else {
+        return 0;
+    }
+}
diff --git a/third_party/nanopb/tests/basic_stream/encode_stream.c b/third_party/nanopb/tests/basic_stream/encode_stream.c
new file mode 100644
index 0000000..7f571c4
--- /dev/null
+++ b/third_party/nanopb/tests/basic_stream/encode_stream.c
@@ -0,0 +1,40 @@
+/* Same as test_encode1.c, except writes directly to stdout.
+ */
+
+#include <stdio.h>
+#include <pb_encode.h>
+#include "person.pb.h"
+#include "test_helpers.h"
+
+/* This binds the pb_ostream_t into the stdout stream */
+bool streamcallback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
+{
+    FILE *file = (FILE*) stream->state;
+    return fwrite(buf, 1, count, file) == count;
+}
+
+int main()
+{
+    /* Initialize the structure with constants */
+    Person person = {"Test Person 99", 99, true, "test@person.com",
+        3, {{"555-12345678", true, Person_PhoneType_MOBILE},
+            {"99-2342", false, 0},
+            {"1234-5678", true, Person_PhoneType_WORK},
+        }};
+    
+    /* Prepare the stream, output goes directly to stdout */
+    pb_ostream_t stream = {&streamcallback, NULL, SIZE_MAX, 0};
+    stream.state = stdout;
+    SET_BINARY_MODE(stdout);
+    
+    /* Now encode it and check if we succeeded. */
+    if (pb_encode(&stream, Person_fields, &person))
+    {
+        return 0; /* Success */
+    }
+    else
+    {
+        fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
+        return 1; /* Failure */
+    }
+}
diff --git a/third_party/nanopb/tests/buffer_only/SConscript b/third_party/nanopb/tests/buffer_only/SConscript
new file mode 100644
index 0000000..55b747b
--- /dev/null
+++ b/third_party/nanopb/tests/buffer_only/SConscript
@@ -0,0 +1,28 @@
+# Run the alltypes test case, but compile with PB_BUFFER_ONLY=1
+
+Import("env")
+
+# Take copy of the files for custom build.
+c = Copy("$TARGET", "$SOURCE")
+env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", c)
+env.Command("alltypes.pb.c", "$BUILD/alltypes/alltypes.pb.c", c)
+env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c)
+env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
+
+# Define the compilation options
+opts = env.Clone()
+opts.Append(CPPDEFINES = {'PB_BUFFER_ONLY': 1})
+
+# Build new version of core
+strict = opts.Clone()
+strict.Append(CFLAGS = strict['CORECFLAGS'])
+strict.Object("pb_decode_bufonly.o", "$NANOPB/pb_decode.c")
+strict.Object("pb_encode_bufonly.o", "$NANOPB/pb_encode.c")
+strict.Object("pb_common_bufonly.o", "$NANOPB/pb_common.c")
+
+# Now build and run the test normally.
+enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_bufonly.o", "pb_common_bufonly.o"])
+dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_bufonly.o", "pb_common_bufonly.o"])
+
+env.RunTest(enc)
+env.RunTest([dec, "encode_alltypes.output"])
diff --git a/third_party/nanopb/tests/callbacks/SConscript b/third_party/nanopb/tests/callbacks/SConscript
new file mode 100644
index 0000000..4452143
--- /dev/null
+++ b/third_party/nanopb/tests/callbacks/SConscript
@@ -0,0 +1,14 @@
+# Test the functionality of the callback fields.
+
+Import("env")
+
+env.NanopbProto("callbacks")
+enc = env.Program(["encode_callbacks.c", "callbacks.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"])
+dec = env.Program(["decode_callbacks.c", "callbacks.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"])
+
+env.RunTest(enc)
+env.RunTest([dec, "encode_callbacks.output"])
+
+env.Decode(["encode_callbacks.output", "callbacks.proto"], MESSAGE = "TestMessage")
+env.Compare(["decode_callbacks.output", "encode_callbacks.decoded"])
+
diff --git a/third_party/nanopb/tests/callbacks/callbacks.proto b/third_party/nanopb/tests/callbacks/callbacks.proto
new file mode 100644
index 0000000..96ac744
--- /dev/null
+++ b/third_party/nanopb/tests/callbacks/callbacks.proto
@@ -0,0 +1,18 @@
+syntax = "proto2";
+
+message SubMessage {
+    optional string stringvalue = 1;
+    repeated int32 int32value = 2;
+    repeated fixed32 fixed32value = 3;
+    repeated fixed64 fixed64value = 4;
+}
+
+message TestMessage {
+    optional string stringvalue = 1;
+    repeated int32 int32value = 2;
+    repeated fixed32 fixed32value = 3;
+    repeated fixed64 fixed64value = 4;
+    optional SubMessage submsg = 5;
+    repeated string repeatedstring = 6;
+}
+
diff --git a/third_party/nanopb/tests/callbacks/decode_callbacks.c b/third_party/nanopb/tests/callbacks/decode_callbacks.c
new file mode 100644
index 0000000..45724d0
--- /dev/null
+++ b/third_party/nanopb/tests/callbacks/decode_callbacks.c
@@ -0,0 +1,97 @@
+/* Decoding testcase for callback fields.
+ * Run e.g. ./test_encode_callbacks | ./test_decode_callbacks
+ */
+
+#include <stdio.h>
+#include <pb_decode.h>
+#include "callbacks.pb.h"
+#include "test_helpers.h"
+
+bool print_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    uint8_t buffer[1024] = {0};
+    
+    /* We could read block-by-block to avoid the large buffer... */
+    if (stream->bytes_left > sizeof(buffer) - 1)
+        return false;
+    
+    if (!pb_read(stream, buffer, stream->bytes_left))
+        return false;
+    
+    /* Print the string, in format comparable with protoc --decode.
+     * Format comes from the arg defined in main().
+     */
+    printf((char*)*arg, buffer);
+    return true;
+}
+
+bool print_int32(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    uint64_t value;
+    if (!pb_decode_varint(stream, &value))
+        return false;
+    
+    printf((char*)*arg, (long)value);
+    return true;
+}
+
+bool print_fixed32(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    uint32_t value;
+    if (!pb_decode_fixed32(stream, &value))
+        return false;
+    
+    printf((char*)*arg, (long)value);
+    return true;
+}
+
+bool print_fixed64(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    uint64_t value;
+    if (!pb_decode_fixed64(stream, &value))
+        return false;
+    
+    printf((char*)*arg, (long)value);
+    return true;
+}
+
+int main()
+{
+    uint8_t buffer[1024];
+    size_t length;
+    pb_istream_t stream;
+    /* Note: empty initializer list initializes the struct with all-0.
+     * This is recommended so that unused callbacks are set to NULL instead
+     * of crashing at runtime.
+     */
+    TestMessage testmessage = {{{NULL}}};
+    
+    SET_BINARY_MODE(stdin);
+    length = fread(buffer, 1, 1024, stdin);
+    stream = pb_istream_from_buffer(buffer, length);    
+    
+    testmessage.submsg.stringvalue.funcs.decode = &print_string;
+    testmessage.submsg.stringvalue.arg = "submsg {\n  stringvalue: \"%s\"\n";
+    testmessage.submsg.int32value.funcs.decode = &print_int32;
+    testmessage.submsg.int32value.arg = "  int32value: %ld\n";
+    testmessage.submsg.fixed32value.funcs.decode = &print_fixed32;
+    testmessage.submsg.fixed32value.arg = "  fixed32value: %ld\n";
+    testmessage.submsg.fixed64value.funcs.decode = &print_fixed64;
+    testmessage.submsg.fixed64value.arg = "  fixed64value: %ld\n}\n";
+    
+    testmessage.stringvalue.funcs.decode = &print_string;
+    testmessage.stringvalue.arg = "stringvalue: \"%s\"\n";
+    testmessage.int32value.funcs.decode = &print_int32;
+    testmessage.int32value.arg = "int32value: %ld\n";
+    testmessage.fixed32value.funcs.decode = &print_fixed32;
+    testmessage.fixed32value.arg = "fixed32value: %ld\n";
+    testmessage.fixed64value.funcs.decode = &print_fixed64;
+    testmessage.fixed64value.arg = "fixed64value: %ld\n";
+    testmessage.repeatedstring.funcs.decode = &print_string;
+    testmessage.repeatedstring.arg = "repeatedstring: \"%s\"\n";
+    
+    if (!pb_decode(&stream, TestMessage_fields, &testmessage))
+        return 1;
+    
+    return 0;
+}
diff --git a/third_party/nanopb/tests/callbacks/encode_callbacks.c b/third_party/nanopb/tests/callbacks/encode_callbacks.c
new file mode 100644
index 0000000..6cb67b1
--- /dev/null
+++ b/third_party/nanopb/tests/callbacks/encode_callbacks.c
@@ -0,0 +1,92 @@
+/* Encoding testcase for callback fields */
+
+#include <stdio.h>
+#include <string.h>
+#include <pb_encode.h>
+#include "callbacks.pb.h"
+#include "test_helpers.h"
+
+bool encode_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    char *str = "Hello world!";
+    
+    if (!pb_encode_tag_for_field(stream, field))
+        return false;
+    
+    return pb_encode_string(stream, (uint8_t*)str, strlen(str));
+}
+
+bool encode_int32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    if (!pb_encode_tag_for_field(stream, field))
+        return false;
+    
+    return pb_encode_varint(stream, 42);
+}
+
+bool encode_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    uint32_t value = 42;
+
+    if (!pb_encode_tag_for_field(stream, field))
+        return false;
+    
+    return pb_encode_fixed32(stream, &value);
+}
+
+bool encode_fixed64(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    uint64_t value = 42;
+
+    if (!pb_encode_tag_for_field(stream, field))
+        return false;
+    
+    return pb_encode_fixed64(stream, &value);
+}
+
+bool encode_repeatedstring(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    char *str[4] = {"Hello world!", "", "Test", "Test2"};
+    int i;
+    
+    for (i = 0; i < 4; i++)
+    {
+        if (!pb_encode_tag_for_field(stream, field))
+            return false;
+        
+        if (!pb_encode_string(stream, (uint8_t*)str[i], strlen(str[i])))
+            return false;
+    }
+    return true;
+}
+
+int main()
+{
+    uint8_t buffer[1024];
+    pb_ostream_t stream;
+    TestMessage testmessage = {{{NULL}}};
+    
+    stream = pb_ostream_from_buffer(buffer, 1024);
+    
+    testmessage.stringvalue.funcs.encode = &encode_string;
+    testmessage.int32value.funcs.encode = &encode_int32;
+    testmessage.fixed32value.funcs.encode = &encode_fixed32;
+    testmessage.fixed64value.funcs.encode = &encode_fixed64;
+    
+    testmessage.has_submsg = true;
+    testmessage.submsg.stringvalue.funcs.encode = &encode_string;
+    testmessage.submsg.int32value.funcs.encode = &encode_int32;
+    testmessage.submsg.fixed32value.funcs.encode = &encode_fixed32;
+    testmessage.submsg.fixed64value.funcs.encode = &encode_fixed64;
+
+    testmessage.repeatedstring.funcs.encode = &encode_repeatedstring;
+    
+    if (!pb_encode(&stream, TestMessage_fields, &testmessage))
+        return 1;
+    
+    SET_BINARY_MODE(stdout);
+    if (fwrite(buffer, stream.bytes_written, 1, stdout) != 1)
+        return 2;
+    
+    return 0;
+}
diff --git a/third_party/nanopb/tests/common/SConscript b/third_party/nanopb/tests/common/SConscript
new file mode 100644
index 0000000..05e2f85
--- /dev/null
+++ b/third_party/nanopb/tests/common/SConscript
@@ -0,0 +1,48 @@
+# Build the common files needed by multiple test cases
+
+Import('env')
+
+# Protocol definitions for the encode/decode_unittests
+env.NanopbProto("unittestproto")
+
+# Protocol definitions for basic_buffer/stream tests
+env.NanopbProto("person")
+
+#--------------------------------------------
+# Binaries of the pb_decode.c and pb_encode.c
+# These are built using more strict warning flags.
+strict = env.Clone()
+strict.Append(CFLAGS = strict['CORECFLAGS'])
+strict.Object("pb_decode.o", "$NANOPB/pb_decode.c")
+strict.Object("pb_encode.o", "$NANOPB/pb_encode.c")
+strict.Object("pb_common.o", "$NANOPB/pb_common.c")
+
+#-----------------------------------------------
+# Binaries of pb_decode etc. with malloc support
+# Uses malloc_wrappers.c to count allocations.
+malloc_env = env.Clone()
+malloc_env.Append(CPPDEFINES = {'PB_ENABLE_MALLOC': 1,
+                                'PB_SYSTEM_HEADER': '\\"malloc_wrappers_syshdr.h\\"'})
+malloc_env.Append(CPPPATH = ["$COMMON"])
+
+if 'SYSHDR' in malloc_env:
+    malloc_env.Append(CPPDEFINES = {'PB_OLD_SYSHDR': malloc_env['SYSHDR']})
+
+# Disable libmudflap, because it will confuse valgrind
+# and other memory leak detection tools.
+if '-fmudflap' in env["CCFLAGS"]:
+    malloc_env["CCFLAGS"].remove("-fmudflap")
+    malloc_env["LINKFLAGS"].remove("-fmudflap")
+    malloc_env["LIBS"].remove("mudflap")
+
+malloc_strict = malloc_env.Clone()
+malloc_strict.Append(CFLAGS = malloc_strict['CORECFLAGS'])
+malloc_strict.Object("pb_decode_with_malloc.o", "$NANOPB/pb_decode.c")
+malloc_strict.Object("pb_encode_with_malloc.o", "$NANOPB/pb_encode.c")
+malloc_strict.Object("pb_common_with_malloc.o", "$NANOPB/pb_common.c")
+
+malloc_env.Object("malloc_wrappers.o", "malloc_wrappers.c")
+malloc_env.Depends("$NANOPB/pb.h", ["malloc_wrappers_syshdr.h", "malloc_wrappers.h"])
+
+Export("malloc_env")
+
diff --git a/third_party/nanopb/tests/common/malloc_wrappers.c b/third_party/nanopb/tests/common/malloc_wrappers.c
new file mode 100644
index 0000000..ad69f1c
--- /dev/null
+++ b/third_party/nanopb/tests/common/malloc_wrappers.c
@@ -0,0 +1,54 @@
+#include "malloc_wrappers.h"
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+static size_t alloc_count = 0;
+
+/* Allocate memory and place check values before and after. */
+void* malloc_with_check(size_t size)
+{
+    size_t size32 = (size + 3) / 4 + 3;
+    uint32_t *buf = malloc(size32 * sizeof(uint32_t));
+    buf[0] = size32;
+    buf[1] = 0xDEADBEEF;
+    buf[size32 - 1] = 0xBADBAD;
+    return buf + 2;
+}
+
+/* Free memory allocated with malloc_with_check() and do the checks. */
+void free_with_check(void *mem)
+{
+    uint32_t *buf = (uint32_t*)mem - 2;
+    assert(buf[1] == 0xDEADBEEF);
+    assert(buf[buf[0] - 1] == 0xBADBAD);
+    free(buf);
+}
+
+/* Track memory usage */
+void* counting_realloc(void *ptr, size_t size)
+{
+    /* Don't allocate crazy amounts of RAM when fuzzing */
+    if (size > 1000000)
+        return NULL;
+
+    if (!ptr && size)
+        alloc_count++;
+    
+    return realloc(ptr, size);
+}
+
+void counting_free(void *ptr)
+{
+    if (ptr)
+    {
+        assert(alloc_count > 0);
+        alloc_count--;
+        free(ptr);
+    }
+}
+
+size_t get_alloc_count()
+{
+    return alloc_count;
+}
diff --git a/third_party/nanopb/tests/common/malloc_wrappers.h b/third_party/nanopb/tests/common/malloc_wrappers.h
new file mode 100644
index 0000000..7eec795
--- /dev/null
+++ b/third_party/nanopb/tests/common/malloc_wrappers.h
@@ -0,0 +1,7 @@
+#include <stdlib.h>
+
+void* malloc_with_check(size_t size);
+void free_with_check(void *mem);
+void* counting_realloc(void *ptr, size_t size);
+void counting_free(void *ptr);
+size_t get_alloc_count();
diff --git a/third_party/nanopb/tests/common/malloc_wrappers_syshdr.h b/third_party/nanopb/tests/common/malloc_wrappers_syshdr.h
new file mode 100644
index 0000000..d295d9e
--- /dev/null
+++ b/third_party/nanopb/tests/common/malloc_wrappers_syshdr.h
@@ -0,0 +1,15 @@
+/* This is just a wrapper in order to get our own malloc wrappers into nanopb core. */
+
+#define pb_realloc(ptr,size) counting_realloc(ptr,size)
+#define pb_free(ptr) counting_free(ptr)
+
+#ifdef PB_OLD_SYSHDR
+#include PB_OLD_SYSHDR
+#else
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#endif
+
+#include <malloc_wrappers.h>
diff --git a/third_party/nanopb/tests/common/person.proto b/third_party/nanopb/tests/common/person.proto
new file mode 100644
index 0000000..becefdf
--- /dev/null
+++ b/third_party/nanopb/tests/common/person.proto
@@ -0,0 +1,22 @@
+syntax = "proto2";
+
+import "nanopb.proto";
+
+message Person {
+  required string name = 1 [(nanopb).max_size = 40];
+  required int32 id = 2;
+  optional string email = 3 [(nanopb).max_size = 40];
+  
+  enum PhoneType {
+    MOBILE = 0;
+    HOME = 1;
+    WORK = 2;
+  }
+
+  message PhoneNumber {
+    required string number = 1 [(nanopb).max_size = 40];
+    optional PhoneType type = 2 [default = HOME];
+  }
+
+  repeated PhoneNumber phone = 4 [(nanopb).max_count = 5];
+}
diff --git a/third_party/nanopb/tests/common/test_helpers.h b/third_party/nanopb/tests/common/test_helpers.h
new file mode 100644
index 0000000..f77760a
--- /dev/null
+++ b/third_party/nanopb/tests/common/test_helpers.h
@@ -0,0 +1,17 @@
+/* Compatibility helpers for the test programs. */
+
+#ifndef _TEST_HELPERS_H_
+#define _TEST_HELPERS_H_
+
+#ifdef _WIN32
+#include <io.h>
+#include <fcntl.h>
+#define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+
+#else
+#define SET_BINARY_MODE(file)
+
+#endif
+
+
+#endif
diff --git a/third_party/nanopb/tests/common/unittestproto.proto b/third_party/nanopb/tests/common/unittestproto.proto
new file mode 100644
index 0000000..23b5b97
--- /dev/null
+++ b/third_party/nanopb/tests/common/unittestproto.proto
@@ -0,0 +1,43 @@
+syntax = "proto2";
+
+import 'nanopb.proto';
+
+message IntegerArray {
+    repeated int32 data = 1 [(nanopb).max_count = 10];
+}
+
+message FloatArray {
+    repeated float data = 1 [(nanopb).max_count = 10];
+}
+
+message StringMessage {
+    required string data = 1 [(nanopb).max_size = 10];
+}
+
+message BytesMessage {
+    required bytes data = 1 [(nanopb).max_size = 16];
+}
+
+message CallbackArray {
+    // We cheat a bit and use this message for testing other types, too.
+    // Nanopb does not care about the actual defined data type for callback
+    // fields.
+    repeated int32 data = 1;
+}
+
+message IntegerContainer {
+    required IntegerArray submsg = 1;
+}
+
+message CallbackContainer {
+    required CallbackArray submsg = 1;
+}
+
+message CallbackContainerContainer {
+    required CallbackContainer submsg = 1;
+}
+
+message StringPointerContainer {
+    repeated string rep_str = 1 [(nanopb).type = FT_POINTER];
+}
+
diff --git a/third_party/nanopb/tests/common/unittests.h b/third_party/nanopb/tests/common/unittests.h
new file mode 100644
index 0000000..c2b470a
--- /dev/null
+++ b/third_party/nanopb/tests/common/unittests.h
@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+#define COMMENT(x) printf("\n----" x "----\n");
+#define STR(x) #x
+#define STR2(x) STR(x)
+#define TEST(x) \
+    if (!(x)) { \
+        fprintf(stderr, "\033[31;1mFAILED:\033[22;39m " __FILE__ ":" STR2(__LINE__) " " #x "\n"); \
+        status = 1; \
+    } else { \
+        printf("\033[32;1mOK:\033[22;39m " #x "\n"); \
+    }
+
+
diff --git a/third_party/nanopb/tests/cxx_main_program/SConscript b/third_party/nanopb/tests/cxx_main_program/SConscript
new file mode 100644
index 0000000..edb8812
--- /dev/null
+++ b/third_party/nanopb/tests/cxx_main_program/SConscript
@@ -0,0 +1,25 @@
+# Run the alltypes test case, but compile it as C++ instead.
+# In fact, compile the entire nanopb using C++ compiler.
+
+Import("env")
+
+# This is needed to get INT32_MIN etc. macros defined
+env = env.Clone()
+env.Append(CPPDEFINES = ['__STDC_LIMIT_MACROS'])
+
+# Copy the files to .cxx extension in order to force C++ build.
+c = Copy("$TARGET", "$SOURCE")
+env.Command("pb_encode.cxx", "#../pb_encode.c", c)
+env.Command("pb_decode.cxx", "#../pb_decode.c", c)
+env.Command("pb_common.cxx", "#../pb_common.c", c)
+env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", c)
+env.Command("alltypes.pb.cxx", "$BUILD/alltypes/alltypes.pb.c", c)
+env.Command("encode_alltypes.cxx", "$BUILD/alltypes/encode_alltypes.c", c)
+env.Command("decode_alltypes.cxx", "$BUILD/alltypes/decode_alltypes.c", c)
+
+# Now build and run the test normally.
+enc = env.Program(["encode_alltypes.cxx", "alltypes.pb.cxx", "pb_encode.cxx", "pb_common.cxx"])
+dec = env.Program(["decode_alltypes.cxx", "alltypes.pb.cxx", "pb_decode.cxx", "pb_common.cxx"])
+
+env.RunTest(enc)
+env.RunTest([dec, "encode_alltypes.output"])
diff --git a/third_party/nanopb/tests/cyclic_messages/SConscript b/third_party/nanopb/tests/cyclic_messages/SConscript
new file mode 100644
index 0000000..c782001
--- /dev/null
+++ b/third_party/nanopb/tests/cyclic_messages/SConscript
@@ -0,0 +1,11 @@
+Import("env")
+
+# Encode cyclic messages with callback fields
+
+c = Copy("$TARGET", "$SOURCE")
+env.Command("cyclic_callback.proto", "cyclic.proto", c)
+env.NanopbProto(["cyclic_callback", "cyclic_callback.options"])
+
+enc_callback = env.Program(["encode_cyclic_callback.c", "cyclic_callback.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"])
+
+
diff --git a/third_party/nanopb/tests/cyclic_messages/cyclic.proto b/third_party/nanopb/tests/cyclic_messages/cyclic.proto
new file mode 100644
index 0000000..8cab0b1
--- /dev/null
+++ b/third_party/nanopb/tests/cyclic_messages/cyclic.proto
@@ -0,0 +1,27 @@
+// Test structures with cyclic references.
+// These can only be handled in pointer/callback mode,
+// see associated .options files.
+
+syntax = "proto2";
+
+message TreeNode
+{
+    optional int32 leaf = 1;
+    optional TreeNode left = 2;
+    optional TreeNode right = 3;
+}
+
+message Dictionary
+{
+    repeated KeyValuePair dictItem = 1;
+}
+
+message KeyValuePair
+{
+    required string key = 1;
+    optional string stringValue = 2;
+    optional int32 intValue = 3;
+    optional Dictionary dictValue = 4;
+    optional TreeNode treeValue = 5;
+}
+
diff --git a/third_party/nanopb/tests/cyclic_messages/cyclic_callback.options b/third_party/nanopb/tests/cyclic_messages/cyclic_callback.options
new file mode 100644
index 0000000..fd4e1e1
--- /dev/null
+++ b/third_party/nanopb/tests/cyclic_messages/cyclic_callback.options
@@ -0,0 +1,7 @@
+TreeNode.left               type:FT_CALLBACK
+TreeNode.right              type:FT_CALLBACK
+
+Dictionary.data             type:FT_CALLBACK
+KeyValuePair.key            max_size:8
+KeyValuePair.stringValue    max_size:8
+KeyValuePair.treeValue      type:FT_CALLBACK
diff --git a/third_party/nanopb/tests/cyclic_messages/encode_cyclic_callback.c b/third_party/nanopb/tests/cyclic_messages/encode_cyclic_callback.c
new file mode 100644
index 0000000..7f67e70
--- /dev/null
+++ b/third_party/nanopb/tests/cyclic_messages/encode_cyclic_callback.c
@@ -0,0 +1,148 @@
+/* This program parses an input string in a format a bit like JSON:
+ * {'foobar': 1234, 'xyz': 'abc', 'tree': [[[1, 2], 3], [4, 5]]}
+ * and encodes it as protobuf
+ *
+ * Note: The string parsing here is not in any way intended to be robust
+ *       nor safe against buffer overflows. It is just for this test.
+ */
+
+#include <pb_encode.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "cyclic_callback.pb.h"
+
+static char *find_end_of_item(char *p)
+{
+    int depth = 0;
+    do {
+        if (*p == '[' || *p == '{') depth++;
+        if (*p == ']' || *p == '}') depth--;
+        p++;
+    } while (depth > 0 || (*p != ',' && *p != '}'));
+    
+    if (*p == '}')
+        return p; /* End of parent dict */
+    
+    p++;
+    while (*p == ' ') p++;
+    return p;
+}
+
+/* Parse a tree in format [[1 2] 3] and encode it directly to protobuf */
+static bool encode_tree(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    TreeNode tree = TreeNode_init_zero;
+    char *p = (char*)*arg;
+    
+    if (*p == '[')
+    {
+        /* This is a tree branch */
+        p++;
+        tree.left.funcs.encode = encode_tree;
+        tree.left.arg = p;
+        
+        p = find_end_of_item(p);
+        tree.right.funcs.encode = encode_tree;
+        tree.right.arg = p;
+    }
+    else
+    {
+        /* This is a leaf node */
+        tree.has_leaf = true;
+        tree.leaf = atoi(p);
+    }
+    
+    return pb_encode_tag_for_field(stream, field) &&
+           pb_encode_submessage(stream, TreeNode_fields, &tree);
+}
+
+/* Parse a dictionary in format {'name': value} and encode it directly to protobuf */
+static bool encode_dictionary(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    int textlen;
+    char *p = (char*)*arg;
+    if (*p == '{') p++;
+    while (*p != '}')
+    {
+        KeyValuePair pair = KeyValuePair_init_zero;
+        
+        if (*p != '\'')
+            PB_RETURN_ERROR(stream, "invalid key, missing quote");
+        
+        p++; /* Starting quote of key */
+        textlen = strchr(p, '\'') - p;
+        strncpy(pair.key, p, textlen);
+        pair.key[textlen] = 0;
+        p += textlen + 2;
+        
+        while (*p == ' ') p++;
+        
+        if (*p == '[')
+        {
+            /* Value is a tree */
+            pair.treeValue.funcs.encode = encode_tree;
+            pair.treeValue.arg = p;
+        }
+        else if (*p == '\'')
+        {
+            /* Value is a string */
+            pair.has_stringValue = true;
+            p++;
+            textlen = strchr(p, '\'') - p;
+            strncpy(pair.stringValue, p, textlen);
+            pair.stringValue[textlen] = 0;
+        }
+        else if (*p == '{')
+        {
+            /* Value is a dictionary */
+            pair.has_dictValue = true;
+            pair.dictValue.dictItem.funcs.encode = encode_dictionary;
+            pair.dictValue.dictItem.arg = p;
+        }
+        else
+        {
+            /* Value is integer */
+            pair.has_intValue = true;
+            pair.intValue = atoi(p);
+        }
+        
+        p = find_end_of_item(p);
+        
+        if (!pb_encode_tag_for_field(stream, field))
+            return false;
+        
+        if (!pb_encode_submessage(stream, KeyValuePair_fields, &pair))
+            return false;
+    }
+
+    return true;
+}
+
+
+int main(int argc, char *argv[])
+{
+    uint8_t buffer[256];
+    pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+    Dictionary dict = Dictionary_init_zero;
+    
+    if (argc <= 1)
+    {
+        fprintf(stderr, "Usage: %s \"{'foobar': 1234, ...}\"\n", argv[0]);
+        return 1;
+    }
+    
+    dict.dictItem.funcs.encode = encode_dictionary;
+    dict.dictItem.arg = argv[1];
+
+    if (!pb_encode(&stream, Dictionary_fields, &dict))
+    {
+        fprintf(stderr, "Encoding error: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    }
+    
+    fwrite(buffer, 1, stream.bytes_written, stdout);
+    return 0;
+}
+
+
diff --git a/third_party/nanopb/tests/decode_unittests/SConscript b/third_party/nanopb/tests/decode_unittests/SConscript
new file mode 100644
index 0000000..369b9dc
--- /dev/null
+++ b/third_party/nanopb/tests/decode_unittests/SConscript
@@ -0,0 +1,4 @@
+Import('env')
+p = env.Program(["decode_unittests.c", "$COMMON/unittestproto.pb.c"])
+env.RunTest(p)
+
diff --git a/third_party/nanopb/tests/decode_unittests/decode_unittests.c b/third_party/nanopb/tests/decode_unittests/decode_unittests.c
new file mode 100644
index 0000000..47f0fbd
--- /dev/null
+++ b/third_party/nanopb/tests/decode_unittests/decode_unittests.c
@@ -0,0 +1,344 @@
+/* This includes the whole .c file to get access to static functions. */
+#define PB_ENABLE_MALLOC
+#include "pb_common.c"
+#include "pb_decode.c"
+
+#include <stdio.h>
+#include <string.h>
+#include "unittests.h"
+#include "unittestproto.pb.h"
+
+#define S(x) pb_istream_from_buffer((uint8_t*)x, sizeof(x) - 1)
+
+bool stream_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
+{
+    if (stream->state != NULL)
+        return false; /* Simulate error */
+    
+    if (buf != NULL)
+        memset(buf, 'x', count);
+    return true;
+}
+
+/* Verifies that the stream passed to callback matches the byte array pointed to by arg. */
+bool callback_check(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+    int i;
+    uint8_t byte;
+    pb_bytes_array_t *ref = (pb_bytes_array_t*) *arg;
+    
+    for (i = 0; i < ref->size; i++)
+    {
+        if (!pb_read(stream, &byte, 1))
+            return false;
+        
+        if (byte != ref->bytes[i])
+            return false;
+    }
+    
+    return true;
+}
+
+int main()
+{
+    int status = 0;
+    
+    {
+        uint8_t buffer1[] = "foobartest1234";
+        uint8_t buffer2[sizeof(buffer1)];
+        pb_istream_t stream = pb_istream_from_buffer(buffer1, sizeof(buffer1));
+        
+        COMMENT("Test pb_read and pb_istream_t");
+        TEST(pb_read(&stream, buffer2, 6))
+        TEST(memcmp(buffer2, "foobar", 6) == 0)
+        TEST(stream.bytes_left == sizeof(buffer1) - 6)
+        TEST(pb_read(&stream, buffer2 + 6, stream.bytes_left))
+        TEST(memcmp(buffer1, buffer2, sizeof(buffer1)) == 0)
+        TEST(stream.bytes_left == 0)
+        TEST(!pb_read(&stream, buffer2, 1))
+    }
+    
+    {
+        uint8_t buffer[20];
+        pb_istream_t stream = {&stream_callback, NULL, 20};
+        
+        COMMENT("Test pb_read with custom callback");
+        TEST(pb_read(&stream, buffer, 5))
+        TEST(memcmp(buffer, "xxxxx", 5) == 0)
+        TEST(!pb_read(&stream, buffer, 50))
+        stream.state = (void*)1; /* Simulated error return from callback */
+        TEST(!pb_read(&stream, buffer, 5))
+        stream.state = NULL;
+        TEST(pb_read(&stream, buffer, 15))
+    }
+    
+    {
+        pb_istream_t s;
+        uint64_t u;
+        int64_t i;
+        
+        COMMENT("Test pb_decode_varint");
+        TEST((s = S("\x00"), pb_decode_varint(&s, &u) && u == 0));
+        TEST((s = S("\x01"), pb_decode_varint(&s, &u) && u == 1));
+        TEST((s = S("\xAC\x02"), pb_decode_varint(&s, &u) && u == 300));
+        TEST((s = S("\xFF\xFF\xFF\xFF\x0F"), pb_decode_varint(&s, &u) && u == UINT32_MAX));
+        TEST((s = S("\xFF\xFF\xFF\xFF\x0F"), pb_decode_varint(&s, (uint64_t*)&i) && i == UINT32_MAX));
+        TEST((s = S("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"),
+              pb_decode_varint(&s, (uint64_t*)&i) && i == -1));
+        TEST((s = S("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"),
+              pb_decode_varint(&s, &u) && u == UINT64_MAX));
+        TEST((s = S("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"),
+              !pb_decode_varint(&s, &u)));
+    }
+    
+    {
+        pb_istream_t s;
+        uint32_t u;
+        
+        COMMENT("Test pb_decode_varint32");
+        TEST((s = S("\x00"), pb_decode_varint32(&s, &u) && u == 0));
+        TEST((s = S("\x01"), pb_decode_varint32(&s, &u) && u == 1));
+        TEST((s = S("\xAC\x02"), pb_decode_varint32(&s, &u) && u == 300));
+        TEST((s = S("\xFF\xFF\xFF\xFF\x0F"), pb_decode_varint32(&s, &u) && u == UINT32_MAX));
+        TEST((s = S("\xFF\xFF\xFF\xFF\xFF\x01"), !pb_decode_varint32(&s, &u)));
+    }
+    
+    {
+        pb_istream_t s;
+        COMMENT("Test pb_skip_varint");
+        TEST((s = S("\x00""foobar"), pb_skip_varint(&s) && s.bytes_left == 6))
+        TEST((s = S("\xAC\x02""foobar"), pb_skip_varint(&s) && s.bytes_left == 6))
+        TEST((s = S("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01""foobar"),
+              pb_skip_varint(&s) && s.bytes_left == 6))
+        TEST((s = S("\xFF"), !pb_skip_varint(&s)))
+    }
+    
+    {
+        pb_istream_t s;
+        COMMENT("Test pb_skip_string")
+        TEST((s = S("\x00""foobar"), pb_skip_string(&s) && s.bytes_left == 6))
+        TEST((s = S("\x04""testfoobar"), pb_skip_string(&s) && s.bytes_left == 6))
+        TEST((s = S("\x04"), !pb_skip_string(&s)))
+        TEST((s = S("\xFF"), !pb_skip_string(&s)))
+    }
+    
+    {
+        pb_istream_t s = S("\x01\x00");
+        pb_field_t f = {1, PB_LTYPE_VARINT, 0, 0, 4, 0, 0};
+        uint32_t d;
+        COMMENT("Test pb_dec_varint using uint32_t")
+        TEST(pb_dec_varint(&s, &f, &d) && d == 1)
+        
+        /* Verify that no more than data_size is written. */
+        d = 0xFFFFFFFF;
+        f.data_size = 1;
+        TEST(pb_dec_varint(&s, &f, &d) && (d == 0xFFFFFF00 || d == 0x00FFFFFF))
+    }
+    
+    {
+        pb_istream_t s;
+        pb_field_t f = {1, PB_LTYPE_SVARINT, 0, 0, 4, 0, 0};
+        int32_t d;
+        
+        COMMENT("Test pb_dec_svarint using int32_t")
+        TEST((s = S("\x01"), pb_dec_svarint(&s, &f, &d) && d == -1))
+        TEST((s = S("\x02"), pb_dec_svarint(&s, &f, &d) && d == 1))
+        TEST((s = S("\xfe\xff\xff\xff\x0f"), pb_dec_svarint(&s, &f, &d) && d == INT32_MAX))
+        TEST((s = S("\xff\xff\xff\xff\x0f"), pb_dec_svarint(&s, &f, &d) && d == INT32_MIN))
+    }
+    
+    {
+        pb_istream_t s;
+        pb_field_t f = {1, PB_LTYPE_SVARINT, 0, 0, 8, 0, 0};
+        uint64_t d;
+        
+        COMMENT("Test pb_dec_svarint using uint64_t")
+        TEST((s = S("\x01"), pb_dec_svarint(&s, &f, &d) && d == -1))
+        TEST((s = S("\x02"), pb_dec_svarint(&s, &f, &d) && d == 1))
+        TEST((s = S("\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"), pb_dec_svarint(&s, &f, &d) && d == INT64_MAX))
+        TEST((s = S("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"), pb_dec_svarint(&s, &f, &d) && d == INT64_MIN))
+    }
+    
+    {
+        pb_istream_t s;
+        pb_field_t f = {1, PB_LTYPE_FIXED32, 0, 0, 4, 0, 0};
+        float d;
+        
+        COMMENT("Test pb_dec_fixed32 using float (failures here may be caused by imperfect rounding)")
+        TEST((s = S("\x00\x00\x00\x00"), pb_dec_fixed32(&s, &f, &d) && d == 0.0f))
+        TEST((s = S("\x00\x00\xc6\x42"), pb_dec_fixed32(&s, &f, &d) && d == 99.0f))
+        TEST((s = S("\x4e\x61\x3c\xcb"), pb_dec_fixed32(&s, &f, &d) && d == -12345678.0f))
+        TEST((s = S("\x00"), !pb_dec_fixed32(&s, &f, &d) && d == -12345678.0f))
+    }
+    
+    {
+        pb_istream_t s;
+        pb_field_t f = {1, PB_LTYPE_FIXED64, 0, 0, 8, 0, 0};
+        double d;
+        
+        COMMENT("Test pb_dec_fixed64 using double (failures here may be caused by imperfect rounding)")
+        TEST((s = S("\x00\x00\x00\x00\x00\x00\x00\x00"), pb_dec_fixed64(&s, &f, &d) && d == 0.0))
+        TEST((s = S("\x00\x00\x00\x00\x00\xc0\x58\x40"), pb_dec_fixed64(&s, &f, &d) && d == 99.0))
+        TEST((s = S("\x00\x00\x00\xc0\x29\x8c\x67\xc1"), pb_dec_fixed64(&s, &f, &d) && d == -12345678.0f))
+    }
+    
+    {
+        pb_istream_t s;
+        struct { pb_size_t size; uint8_t bytes[5]; } d;
+        pb_field_t f = {1, PB_LTYPE_BYTES, 0, 0, sizeof(d), 0, 0};
+        
+        COMMENT("Test pb_dec_bytes")
+        TEST((s = S("\x00"), pb_dec_bytes(&s, &f, &d) && d.size == 0))
+        TEST((s = S("\x01\xFF"), pb_dec_bytes(&s, &f, &d) && d.size == 1 && d.bytes[0] == 0xFF))
+        TEST((s = S("\x05xxxxx"), pb_dec_bytes(&s, &f, &d) && d.size == 5))
+        TEST((s = S("\x05xxxx"), !pb_dec_bytes(&s, &f, &d)))
+        
+        /* Note: the size limit on bytes-fields is not strictly obeyed, as
+         * the compiler may add some padding to the struct. Using this padding
+         * is not a very good thing to do, but it is difficult to avoid when
+         * we use only a single uint8_t to store the size of the field.
+         * Therefore this tests against a 10-byte string, while otherwise even
+         * 6 bytes should error out.
+         */
+        TEST((s = S("\x10xxxxxxxxxx"), !pb_dec_bytes(&s, &f, &d)))
+    }
+    
+    {
+        pb_istream_t s;
+        pb_field_t f = {1, PB_LTYPE_STRING, 0, 0, 5, 0, 0};
+        char d[5];
+        
+        COMMENT("Test pb_dec_string")
+        TEST((s = S("\x00"), pb_dec_string(&s, &f, &d) && d[0] == '\0'))
+        TEST((s = S("\x04xyzz"), pb_dec_string(&s, &f, &d) && strcmp(d, "xyzz") == 0))
+        TEST((s = S("\x05xyzzy"), !pb_dec_string(&s, &f, &d)))
+    }
+    
+    {
+        pb_istream_t s;
+        IntegerArray dest;
+        
+        COMMENT("Testing pb_decode with repeated int32 field")
+        TEST((s = S(""), pb_decode(&s, IntegerArray_fields, &dest) && dest.data_count == 0))
+        TEST((s = S("\x08\x01\x08\x02"), pb_decode(&s, IntegerArray_fields, &dest)
+             && dest.data_count == 2 && dest.data[0] == 1 && dest.data[1] == 2))
+        s = S("\x08\x01\x08\x02\x08\x03\x08\x04\x08\x05\x08\x06\x08\x07\x08\x08\x08\x09\x08\x0A");
+        TEST(pb_decode(&s, IntegerArray_fields, &dest) && dest.data_count == 10 && dest.data[9] == 10)
+        s = S("\x08\x01\x08\x02\x08\x03\x08\x04\x08\x05\x08\x06\x08\x07\x08\x08\x08\x09\x08\x0A\x08\x0B");
+        TEST(!pb_decode(&s, IntegerArray_fields, &dest))
+    }
+    
+    {
+        pb_istream_t s;
+        IntegerArray dest;
+        
+        COMMENT("Testing pb_decode with packed int32 field")
+        TEST((s = S("\x0A\x00"), pb_decode(&s, IntegerArray_fields, &dest)
+            && dest.data_count == 0))
+        TEST((s = S("\x0A\x01\x01"), pb_decode(&s, IntegerArray_fields, &dest)
+            && dest.data_count == 1 && dest.data[0] == 1))
+        TEST((s = S("\x0A\x0A\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A"), pb_decode(&s, IntegerArray_fields, &dest)
+            && dest.data_count == 10 && dest.data[0] == 1 && dest.data[9] == 10))
+        TEST((s = S("\x0A\x0B\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"), !pb_decode(&s, IntegerArray_fields, &dest)))
+        
+        /* Test invalid wire data */
+        TEST((s = S("\x0A\xFF"), !pb_decode(&s, IntegerArray_fields, &dest)))
+        TEST((s = S("\x0A\x01"), !pb_decode(&s, IntegerArray_fields, &dest)))
+    }
+    
+    {
+        pb_istream_t s;
+        IntegerArray dest;
+        
+        COMMENT("Testing pb_decode with unknown fields")
+        TEST((s = S("\x18\x0F\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest)
+            && dest.data_count == 1 && dest.data[0] == 1))
+        TEST((s = S("\x19\x00\x00\x00\x00\x00\x00\x00\x00\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest)
+            && dest.data_count == 1 && dest.data[0] == 1))
+        TEST((s = S("\x1A\x00\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest)
+            && dest.data_count == 1 && dest.data[0] == 1))
+        TEST((s = S("\x1B\x08\x01"), !pb_decode(&s, IntegerArray_fields, &dest)))
+        TEST((s = S("\x1D\x00\x00\x00\x00\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest)
+            && dest.data_count == 1 && dest.data[0] == 1))
+    }
+    
+    {
+        pb_istream_t s;
+        CallbackArray dest;
+        struct { pb_size_t size; uint8_t bytes[10]; } ref;
+        dest.data.funcs.decode = &callback_check;
+        dest.data.arg = &ref;
+        
+        COMMENT("Testing pb_decode with callbacks")
+        /* Single varint */
+        ref.size = 1; ref.bytes[0] = 0x55;
+        TEST((s = S("\x08\x55"), pb_decode(&s, CallbackArray_fields, &dest)))
+        /* Packed varint */
+        ref.size = 3; ref.bytes[0] = ref.bytes[1] = ref.bytes[2] = 0x55;
+        TEST((s = S("\x0A\x03\x55\x55\x55"), pb_decode(&s, CallbackArray_fields, &dest)))
+        /* Packed varint with loop */
+        ref.size = 1; ref.bytes[0] = 0x55;
+        TEST((s = S("\x0A\x03\x55\x55\x55"), pb_decode(&s, CallbackArray_fields, &dest)))
+        /* Single fixed32 */
+        ref.size = 4; ref.bytes[0] = ref.bytes[1] = ref.bytes[2] = ref.bytes[3] = 0xAA;
+        TEST((s = S("\x0D\xAA\xAA\xAA\xAA"), pb_decode(&s, CallbackArray_fields, &dest)))
+        /* Single fixed64 */
+        ref.size = 8; memset(ref.bytes, 0xAA, 8);
+        TEST((s = S("\x09\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"), pb_decode(&s, CallbackArray_fields, &dest)))
+        /* Unsupported field type */
+        TEST((s = S("\x0B\x00"), !pb_decode(&s, CallbackArray_fields, &dest)))
+        
+        /* Just make sure that our test function works */
+        ref.size = 1; ref.bytes[0] = 0x56;
+        TEST((s = S("\x08\x55"), !pb_decode(&s, CallbackArray_fields, &dest)))
+    }
+    
+    {
+        pb_istream_t s;
+        IntegerArray dest;
+        
+        COMMENT("Testing pb_decode message termination")
+        TEST((s = S(""), pb_decode(&s, IntegerArray_fields, &dest)))
+        TEST((s = S("\x00"), pb_decode(&s, IntegerArray_fields, &dest)))
+        TEST((s = S("\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest)))
+        TEST((s = S("\x08\x01\x00"), pb_decode(&s, IntegerArray_fields, &dest)))
+        TEST((s = S("\x08"), !pb_decode(&s, IntegerArray_fields, &dest)))
+    }
+    
+    {
+        pb_istream_t s;
+        IntegerContainer dest = {{0}};
+        
+        COMMENT("Testing pb_decode_delimited")
+        TEST((s = S("\x09\x0A\x07\x0A\x05\x01\x02\x03\x04\x05"),
+              pb_decode_delimited(&s, IntegerContainer_fields, &dest)) &&
+              dest.submsg.data_count == 5)
+    }
+    
+    {
+        pb_istream_t s = {0};
+        void *data = NULL;
+        
+        COMMENT("Testing allocate_field")
+        TEST(allocate_field(&s, &data, 10, 10) && data != NULL);
+        TEST(allocate_field(&s, &data, 10, 20) && data != NULL);
+        
+        {
+            void *oldvalue = data;
+            size_t very_big = (size_t)-1;
+            size_t somewhat_big = very_big / 2 + 1;
+            size_t not_so_big = (size_t)1 << (4 * sizeof(size_t));
+        
+            TEST(!allocate_field(&s, &data, very_big, 2) && data == oldvalue);
+            TEST(!allocate_field(&s, &data, somewhat_big, 2) && data == oldvalue);
+            TEST(!allocate_field(&s, &data, not_so_big, not_so_big) && data == oldvalue);
+        }
+        
+        pb_free(data);
+    }
+    
+    if (status != 0)
+        fprintf(stdout, "\n\nSome tests FAILED!\n");
+    
+    return status;
+}
diff --git a/third_party/nanopb/tests/encode_unittests/SConscript b/third_party/nanopb/tests/encode_unittests/SConscript
new file mode 100644
index 0000000..bf6d140
--- /dev/null
+++ b/third_party/nanopb/tests/encode_unittests/SConscript
@@ -0,0 +1,5 @@
+# Build and run the stand-alone unit tests for the nanopb encoder part.
+
+Import('env')
+p = env.Program(["encode_unittests.c", "$COMMON/unittestproto.pb.c"])
+env.RunTest(p)
diff --git a/third_party/nanopb/tests/encode_unittests/encode_unittests.c b/third_party/nanopb/tests/encode_unittests/encode_unittests.c
new file mode 100644
index 0000000..583af5c
--- /dev/null
+++ b/third_party/nanopb/tests/encode_unittests/encode_unittests.c
@@ -0,0 +1,355 @@
+/* This includes the whole .c file to get access to static functions. */
+#include "pb_common.c"
+#include "pb_encode.c"
+
+#include <stdio.h>
+#include <string.h>
+#include "unittests.h"
+#include "unittestproto.pb.h"
+
+bool streamcallback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
+{
+    /* Allow only 'x' to be written */
+    while (count--)
+    {
+        if (*buf++ != 'x')
+            return false;
+    }
+    return true;
+}
+
+bool fieldcallback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    int value = 0x55;
+    if (!pb_encode_tag_for_field(stream, field))
+        return false;
+    return pb_encode_varint(stream, value);
+}
+
+bool crazyfieldcallback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+    /* This callback writes different amount of data the second time. */
+    uint32_t *state = (uint32_t*)arg;
+    *state <<= 8;
+    if (!pb_encode_tag_for_field(stream, field))
+        return false;
+    return pb_encode_varint(stream, *state);
+}
+
+/* Check that expression x writes data y.
+ * Y is a string, which may contain null bytes. Null terminator is ignored.
+ */
+#define WRITES(x, y) \
+memset(buffer, 0xAA, sizeof(buffer)), \
+s = pb_ostream_from_buffer(buffer, sizeof(buffer)), \
+(x) && \
+memcmp(buffer, y, sizeof(y) - 1) == 0 && \
+buffer[sizeof(y) - 1] == 0xAA
+
+int main()
+{
+    int status = 0;
+    
+    {
+        uint8_t buffer1[] = "foobartest1234";
+        uint8_t buffer2[sizeof(buffer1)];
+        pb_ostream_t stream = pb_ostream_from_buffer(buffer2, sizeof(buffer1));
+        
+        COMMENT("Test pb_write and pb_ostream_t");
+        TEST(pb_write(&stream, buffer1, sizeof(buffer1)));
+        TEST(memcmp(buffer1, buffer2, sizeof(buffer1)) == 0);
+        TEST(!pb_write(&stream, buffer1, 1));
+        TEST(stream.bytes_written == sizeof(buffer1));
+    }
+    
+    {
+        uint8_t buffer1[] = "xxxxxxx";
+        pb_ostream_t stream = {&streamcallback, 0, SIZE_MAX, 0};
+        
+        COMMENT("Test pb_write with custom callback");
+        TEST(pb_write(&stream, buffer1, 5));
+        buffer1[0] = 'a';
+        TEST(!pb_write(&stream, buffer1, 5));
+    }
+    
+    {
+        uint8_t buffer[30];
+        pb_ostream_t s;
+        
+        COMMENT("Test pb_encode_varint")
+        TEST(WRITES(pb_encode_varint(&s, 0), "\0"));
+        TEST(WRITES(pb_encode_varint(&s, 1), "\1"));
+        TEST(WRITES(pb_encode_varint(&s, 0x7F), "\x7F"));
+        TEST(WRITES(pb_encode_varint(&s, 0x80), "\x80\x01"));
+        TEST(WRITES(pb_encode_varint(&s, UINT32_MAX), "\xFF\xFF\xFF\xFF\x0F"));
+        TEST(WRITES(pb_encode_varint(&s, UINT64_MAX), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"));
+    }
+    
+    {
+        uint8_t buffer[30];
+        pb_ostream_t s;
+        
+        COMMENT("Test pb_encode_tag")
+        TEST(WRITES(pb_encode_tag(&s, PB_WT_STRING, 5), "\x2A"));
+        TEST(WRITES(pb_encode_tag(&s, PB_WT_VARINT, 99), "\x98\x06"));
+    }
+    
+    {
+        uint8_t buffer[30];
+        pb_ostream_t s;
+        pb_field_t field = {10, PB_LTYPE_SVARINT};
+        
+        COMMENT("Test pb_encode_tag_for_field")
+        TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x50"));
+        
+        field.type = PB_LTYPE_FIXED64;
+        TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x51"));
+        
+        field.type = PB_LTYPE_STRING;
+        TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x52"));
+        
+        field.type = PB_LTYPE_FIXED32;
+        TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x55"));
+    }
+    
+    {
+        uint8_t buffer[30];
+        pb_ostream_t s;
+        
+        COMMENT("Test pb_encode_string")
+        TEST(WRITES(pb_encode_string(&s, (const uint8_t*)"abcd", 4), "\x04""abcd"));
+        TEST(WRITES(pb_encode_string(&s, (const uint8_t*)"abcd\x00", 5), "\x05""abcd\x00"));
+        TEST(WRITES(pb_encode_string(&s, (const uint8_t*)"", 0), "\x00"));
+    }
+    
+    {
+        uint8_t buffer[30];
+        pb_ostream_t s;
+        uint8_t value = 1;
+        int32_t max = INT32_MAX;
+        int32_t min = INT32_MIN;
+        int64_t lmax = INT64_MAX;
+        int64_t lmin = INT64_MIN;
+        pb_field_t field = {1, PB_LTYPE_VARINT, 0, 0, sizeof(value)};
+        
+        COMMENT("Test pb_enc_varint and pb_enc_svarint")
+        TEST(WRITES(pb_enc_varint(&s, &field, &value), "\x01"));
+        
+        field.data_size = sizeof(max);
+        TEST(WRITES(pb_enc_svarint(&s, &field, &max), "\xfe\xff\xff\xff\x0f"));
+        TEST(WRITES(pb_enc_svarint(&s, &field, &min), "\xff\xff\xff\xff\x0f"));
+        
+        field.data_size = sizeof(lmax);
+        TEST(WRITES(pb_enc_svarint(&s, &field, &lmax), "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"));
+        TEST(WRITES(pb_enc_svarint(&s, &field, &lmin), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"));
+    }
+    
+    {
+        uint8_t buffer[30];
+        pb_ostream_t s;
+        float fvalue;
+        double dvalue;
+        
+        COMMENT("Test pb_enc_fixed32 using float")
+        fvalue = 0.0f;
+        TEST(WRITES(pb_enc_fixed32(&s, NULL, &fvalue), "\x00\x00\x00\x00"))
+        fvalue = 99.0f;
+        TEST(WRITES(pb_enc_fixed32(&s, NULL, &fvalue), "\x00\x00\xc6\x42"))
+        fvalue = -12345678.0f;
+        TEST(WRITES(pb_enc_fixed32(&s, NULL, &fvalue), "\x4e\x61\x3c\xcb"))
+    
+        COMMENT("Test pb_enc_fixed64 using double")
+        dvalue = 0.0;
+        TEST(WRITES(pb_enc_fixed64(&s, NULL, &dvalue), "\x00\x00\x00\x00\x00\x00\x00\x00"))
+        dvalue = 99.0;
+        TEST(WRITES(pb_enc_fixed64(&s, NULL, &dvalue), "\x00\x00\x00\x00\x00\xc0\x58\x40"))
+        dvalue = -12345678.0;
+        TEST(WRITES(pb_enc_fixed64(&s, NULL, &dvalue), "\x00\x00\x00\xc0\x29\x8c\x67\xc1"))
+    }
+    
+    {
+        uint8_t buffer[30];
+        pb_ostream_t s;
+        struct { pb_size_t size; uint8_t bytes[5]; } value = {5, {'x', 'y', 'z', 'z', 'y'}};
+    
+        COMMENT("Test pb_enc_bytes")
+        TEST(WRITES(pb_enc_bytes(&s, &BytesMessage_fields[0], &value), "\x05xyzzy"))
+        value.size = 0;
+        TEST(WRITES(pb_enc_bytes(&s, &BytesMessage_fields[0], &value), "\x00"))
+    }
+    
+    {
+        uint8_t buffer[30];
+        pb_ostream_t s;
+        char value[30] = "xyzzy";
+        
+        COMMENT("Test pb_enc_string")
+        TEST(WRITES(pb_enc_string(&s, &StringMessage_fields[0], &value), "\x05xyzzy"))
+        value[0] = '\0';
+        TEST(WRITES(pb_enc_string(&s, &StringMessage_fields[0], &value), "\x00"))
+        memset(value, 'x', 30);
+        TEST(WRITES(pb_enc_string(&s, &StringMessage_fields[0], &value), "\x0Axxxxxxxxxx"))
+    }
+    
+    {
+        uint8_t buffer[10];
+        pb_ostream_t s;
+        IntegerArray msg = {5, {1, 2, 3, 4, 5}};
+        
+        COMMENT("Test pb_encode with int32 array")
+        
+        TEST(WRITES(pb_encode(&s, IntegerArray_fields, &msg), "\x0A\x05\x01\x02\x03\x04\x05"))
+        
+        msg.data_count = 0;
+        TEST(WRITES(pb_encode(&s, IntegerArray_fields, &msg), ""))
+        
+        msg.data_count = 10;
+        TEST(!pb_encode(&s, IntegerArray_fields, &msg))
+    }
+    
+    {
+        uint8_t buffer[10];
+        pb_ostream_t s;
+        FloatArray msg = {1, {99.0f}};
+        
+        COMMENT("Test pb_encode with float array")
+        
+        TEST(WRITES(pb_encode(&s, FloatArray_fields, &msg),
+                    "\x0A\x04\x00\x00\xc6\x42"))
+        
+        msg.data_count = 0;
+        TEST(WRITES(pb_encode(&s, FloatArray_fields, &msg), ""))
+        
+        msg.data_count = 3;
+        TEST(!pb_encode(&s, FloatArray_fields, &msg))
+    }
+    
+    {
+        uint8_t buffer[50];
+        pb_ostream_t s;
+        FloatArray msg = {1, {99.0f}};
+        
+        COMMENT("Test array size limit in pb_encode")
+        
+        s = pb_ostream_from_buffer(buffer, sizeof(buffer));
+        TEST((msg.data_count = 10) && pb_encode(&s, FloatArray_fields, &msg))
+        
+        s = pb_ostream_from_buffer(buffer, sizeof(buffer));
+        TEST((msg.data_count = 11) && !pb_encode(&s, FloatArray_fields, &msg))
+    }
+    
+    {
+        uint8_t buffer[10];
+        pb_ostream_t s;
+        CallbackArray msg;
+        
+        msg.data.funcs.encode = &fieldcallback;
+        
+        COMMENT("Test pb_encode with callback field.")
+        TEST(WRITES(pb_encode(&s, CallbackArray_fields, &msg), "\x08\x55"))
+    }
+    
+    {
+        uint8_t buffer[10];
+        pb_ostream_t s;
+        IntegerContainer msg = {{5, {1,2,3,4,5}}};
+        
+        COMMENT("Test pb_encode with packed array in a submessage.")
+        TEST(WRITES(pb_encode(&s, IntegerContainer_fields, &msg),
+                    "\x0A\x07\x0A\x05\x01\x02\x03\x04\x05"))
+    }
+    
+    {
+        uint8_t buffer[32];
+        pb_ostream_t s;
+        BytesMessage msg = {{3, "xyz"}};
+        
+        COMMENT("Test pb_encode with bytes message.")
+        TEST(WRITES(pb_encode(&s, BytesMessage_fields, &msg),
+                    "\x0A\x03xyz"))
+        
+        msg.data.size = 17; /* More than maximum */
+        TEST(!pb_encode(&s, BytesMessage_fields, &msg))
+    }
+        
+    
+    {
+        uint8_t buffer[20];
+        pb_ostream_t s;
+        IntegerContainer msg = {{5, {1,2,3,4,5}}};
+        
+        COMMENT("Test pb_encode_delimited.")
+        TEST(WRITES(pb_encode_delimited(&s, IntegerContainer_fields, &msg),
+                    "\x09\x0A\x07\x0A\x05\x01\x02\x03\x04\x05"))
+    }
+
+    {
+        IntegerContainer msg = {{5, {1,2,3,4,5}}};
+        size_t size;
+        
+        COMMENT("Test pb_get_encoded_size.")
+        TEST(pb_get_encoded_size(&size, IntegerContainer_fields, &msg) &&
+             size == 9);
+    }
+    
+    {
+        uint8_t buffer[10];
+        pb_ostream_t s;
+        CallbackContainer msg;
+        CallbackContainerContainer msg2;
+        uint32_t state = 1;
+        
+        msg.submsg.data.funcs.encode = &fieldcallback;
+        msg2.submsg.submsg.data.funcs.encode = &fieldcallback;
+        
+        COMMENT("Test pb_encode with callback field in a submessage.")
+        TEST(WRITES(pb_encode(&s, CallbackContainer_fields, &msg), "\x0A\x02\x08\x55"))
+        TEST(WRITES(pb_encode(&s, CallbackContainerContainer_fields, &msg2),
+                    "\x0A\x04\x0A\x02\x08\x55"))
+        
+        /* Misbehaving callback: varying output between calls */
+        msg.submsg.data.funcs.encode = &crazyfieldcallback;
+        msg.submsg.data.arg = &state;
+        msg2.submsg.submsg.data.funcs.encode = &crazyfieldcallback;
+        msg2.submsg.submsg.data.arg = &state;
+        
+        TEST(!pb_encode(&s, CallbackContainer_fields, &msg))
+        state = 1;
+        TEST(!pb_encode(&s, CallbackContainerContainer_fields, &msg2))
+    }
+    
+    {
+        uint8_t buffer[StringMessage_size];
+        pb_ostream_t s;
+        StringMessage msg = {"0123456789"};
+        
+        s = pb_ostream_from_buffer(buffer, sizeof(buffer));
+        
+        COMMENT("Test that StringMessage_size is correct")
+
+        TEST(pb_encode(&s, StringMessage_fields, &msg));
+        TEST(s.bytes_written == StringMessage_size);
+    }
+    
+    {
+        uint8_t buffer[128];
+        pb_ostream_t s;
+        StringPointerContainer msg = StringPointerContainer_init_zero;
+        char *strs[1] = {NULL};
+        char zstr[] = "Z";
+        
+        COMMENT("Test string pointer encoding.");
+        
+        msg.rep_str = strs;
+        msg.rep_str_count = 1;
+        TEST(WRITES(pb_encode(&s, StringPointerContainer_fields, &msg), "\x0a\x00"))
+        
+        strs[0] = zstr;
+        TEST(WRITES(pb_encode(&s, StringPointerContainer_fields, &msg), "\x0a\x01Z"))
+    }
+    
+    if (status != 0)
+        fprintf(stdout, "\n\nSome tests FAILED!\n");
+    
+    return status;
+}
diff --git a/third_party/nanopb/tests/enum_sizes/SConscript b/third_party/nanopb/tests/enum_sizes/SConscript
new file mode 100644
index 0000000..048592e
--- /dev/null
+++ b/third_party/nanopb/tests/enum_sizes/SConscript
@@ -0,0 +1,12 @@
+# Test that different sizes of enum fields are properly encoded and decoded.
+
+Import('env')
+
+env.NanopbProto('enumsizes')
+
+p = env.Program(["enumsizes_unittests.c",
+                 "enumsizes.pb.c",
+                 "$COMMON/pb_encode.o",
+                 "$COMMON/pb_decode.o",
+                 "$COMMON/pb_common.o"])
+env.RunTest(p)
diff --git a/third_party/nanopb/tests/enum_sizes/enumsizes.proto b/third_party/nanopb/tests/enum_sizes/enumsizes.proto
new file mode 100644
index 0000000..a85d416
--- /dev/null
+++ b/third_party/nanopb/tests/enum_sizes/enumsizes.proto
@@ -0,0 +1,86 @@
+/* Test handling of enums with different value ranges.
+ * Depending on compiler and the packed_enum setting, the datatypes
+ * for enums can be either signed or unsigned. In past this has caused
+ * a bit of a problem for the encoder/decoder (issue #164).
+ */
+
+syntax = "proto2";
+
+import 'nanopb.proto';
+
+option (nanopb_fileopt).long_names = false;
+
+enum UnpackedUint8 {
+    option (nanopb_enumopt).packed_enum = false;
+    UU8_MIN = 0;
+    UU8_MAX = 255;
+}
+
+enum PackedUint8 {
+    option (nanopb_enumopt).packed_enum = true;
+    PU8_MIN = 0;
+    PU8_MAX = 255;
+}
+
+enum UnpackedInt8 {
+    option (nanopb_enumopt).packed_enum = false;
+    UI8_MIN = -128;
+    UI8_MAX = 127;
+}
+
+enum PackedInt8 {
+    option (nanopb_enumopt).packed_enum = true;
+    PI8_MIN = -128;
+    PI8_MAX = 127;
+}
+
+enum UnpackedUint16 {
+    option (nanopb_enumopt).packed_enum = false;
+    UU16_MIN = 0;
+    UU16_MAX = 65535;
+}
+
+enum PackedUint16 {
+    option (nanopb_enumopt).packed_enum = true;
+    PU16_MIN = 0;
+    PU16_MAX = 65535;
+}
+
+enum UnpackedInt16 {
+    option (nanopb_enumopt).packed_enum = false;
+    UI16_MIN = -32768;
+    UI16_MAX = 32767;
+}
+
+enum PackedInt16 {
+    option (nanopb_enumopt).packed_enum = true;
+    PI16_MIN = -32768;
+    PI16_MAX = 32767;
+}
+
+/* Protobuf supports enums up to 32 bits.
+ * The 32 bit case is covered by HugeEnum in the alltypes test.
+ */
+
+message PackedEnums {
+    required PackedUint8  u8_min  = 1;
+    required PackedUint8  u8_max  = 2;
+    required PackedInt8   i8_min  = 3;
+    required PackedInt8   i8_max  = 4;
+    required PackedUint16 u16_min = 5;
+    required PackedUint16 u16_max = 6;
+    required PackedInt16  i16_min = 7;
+    required PackedInt16  i16_max = 8;
+}
+
+message UnpackedEnums {
+    required UnpackedUint8  u8_min  = 1;
+    required UnpackedUint8  u8_max  = 2;
+    required UnpackedInt8   i8_min  = 3;
+    required UnpackedInt8   i8_max  = 4;
+    required UnpackedUint16 u16_min = 5;
+    required UnpackedUint16 u16_max = 6;
+    required UnpackedInt16  i16_min = 7;
+    required UnpackedInt16  i16_max = 8;
+}
+
diff --git a/third_party/nanopb/tests/enum_sizes/enumsizes_unittests.c b/third_party/nanopb/tests/enum_sizes/enumsizes_unittests.c
new file mode 100644
index 0000000..5606895
--- /dev/null
+++ b/third_party/nanopb/tests/enum_sizes/enumsizes_unittests.c
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <string.h>
+#include <pb_decode.h>
+#include <pb_encode.h>
+#include "unittests.h"
+#include "enumsizes.pb.h"
+
+int main()
+{
+    int status = 0;
+
+    UnpackedEnums msg1 = {
+        UU8_MIN,  UU8_MAX,
+        UI8_MIN,  UI8_MAX,
+        UU16_MIN, UU16_MAX,
+        UI16_MIN, UI16_MAX,
+    };
+    
+    PackedEnums msg2;
+    UnpackedEnums msg3;
+    uint8_t buf[256];
+    size_t msgsize;
+    
+    COMMENT("Step 1: unpacked enums -> protobuf");
+    {
+        pb_ostream_t s = pb_ostream_from_buffer(buf, sizeof(buf));
+        TEST(pb_encode(&s, UnpackedEnums_fields, &msg1));
+        msgsize = s.bytes_written;
+    }
+    
+    COMMENT("Step 2: protobuf -> packed enums");
+    {
+        pb_istream_t s = pb_istream_from_buffer(buf, msgsize);
+        TEST(pb_decode(&s, PackedEnums_fields, &msg2));
+        
+        TEST(msg1.u8_min  == (int)msg2.u8_min);
+        TEST(msg1.u8_max  == (int)msg2.u8_max);
+        TEST(msg1.i8_min  == (int)msg2.i8_min);
+        TEST(msg1.i8_max  == (int)msg2.i8_max);
+        TEST(msg1.u16_min == (int)msg2.u16_min);
+        TEST(msg1.u16_max == (int)msg2.u16_max);
+        TEST(msg1.i16_min == (int)msg2.i16_min);
+        TEST(msg1.i16_max == (int)msg2.i16_max);
+    }
+    
+    COMMENT("Step 3: packed enums -> protobuf");
+    {
+        pb_ostream_t s = pb_ostream_from_buffer(buf, sizeof(buf));
+        TEST(pb_encode(&s, PackedEnums_fields, &msg2));
+        msgsize = s.bytes_written;
+    }
+    
+    COMMENT("Step 4: protobuf -> unpacked enums");
+    {
+        pb_istream_t s = pb_istream_from_buffer(buf, msgsize);
+        TEST(pb_decode(&s, UnpackedEnums_fields, &msg3));
+
+        TEST(msg1.u8_min  == (int)msg3.u8_min);
+        TEST(msg1.u8_max  == (int)msg3.u8_max);
+        TEST(msg1.i8_min  == (int)msg3.i8_min);
+        TEST(msg1.i8_max  == (int)msg3.i8_max);
+        TEST(msg1.u16_min == (int)msg2.u16_min);
+        TEST(msg1.u16_max == (int)msg2.u16_max);
+        TEST(msg1.i16_min == (int)msg2.i16_min);
+        TEST(msg1.i16_max == (int)msg2.i16_max);
+    }
+
+    if (status != 0)
+        fprintf(stdout, "\n\nSome tests FAILED!\n");
+
+    return status;
+}
diff --git a/third_party/nanopb/tests/extensions/SConscript b/third_party/nanopb/tests/extensions/SConscript
new file mode 100644
index 0000000..a2c8742
--- /dev/null
+++ b/third_party/nanopb/tests/extensions/SConscript
@@ -0,0 +1,16 @@
+# Test the support for extension fields.
+
+Import("env")
+
+# We use the files from the alltypes test case
+incpath = env.Clone()
+incpath.Append(PROTOCPATH = '$BUILD/alltypes')
+incpath.Append(CPPPATH = '$BUILD/alltypes')
+
+incpath.NanopbProto(["extensions", "extensions.options"])
+enc = incpath.Program(["encode_extensions.c", "extensions.pb.c", "$BUILD/alltypes/alltypes.pb$OBJSUFFIX", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"])
+dec = incpath.Program(["decode_extensions.c", "extensions.pb.c", "$BUILD/alltypes/alltypes.pb$OBJSUFFIX", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"])
+
+env.RunTest(enc)
+env.RunTest([dec, "encode_extensions.output"])
+
diff --git a/third_party/nanopb/tests/extensions/decode_extensions.c b/third_party/nanopb/tests/extensions/decode_extensions.c
new file mode 100644
index 0000000..e437438
--- /dev/null
+++ b/third_party/nanopb/tests/extensions/decode_extensions.c
@@ -0,0 +1,60 @@
+/* Test decoding of extension fields. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pb_decode.h>
+#include "alltypes.pb.h"
+#include "extensions.pb.h"
+#include "test_helpers.h"
+
+#define TEST(x) if (!(x)) { \
+    printf("Test " #x " failed.\n"); \
+    return 2; \
+    }
+
+int main(int argc, char **argv)
+{
+    uint8_t buffer[1024];
+    size_t count;
+    pb_istream_t stream;
+    
+    AllTypes alltypes = {0};
+    int32_t extensionfield1;
+    pb_extension_t ext1;
+    ExtensionMessage extensionfield2;
+    pb_extension_t ext2;
+    
+    /* Read the message data */
+    SET_BINARY_MODE(stdin);
+    count = fread(buffer, 1, sizeof(buffer), stdin);
+    stream = pb_istream_from_buffer(buffer, count);
+    
+    /* Add the extensions */
+    alltypes.extensions = &ext1;        
+
+    ext1.type = &AllTypes_extensionfield1;
+    ext1.dest = &extensionfield1;
+    ext1.next = &ext2;
+    
+    ext2.type = &ExtensionMessage_AllTypes_extensionfield2;
+    ext2.dest = &extensionfield2;
+    ext2.next = NULL;
+    
+    /* Decode the message */
+    if (!pb_decode(&stream, AllTypes_fields, &alltypes))
+    {
+        printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    }
+
+    /* Check that the extensions decoded properly */
+    TEST(ext1.found)
+    TEST(extensionfield1 == 12345)
+    TEST(ext2.found)
+    TEST(strcmp(extensionfield2.test1, "test") == 0)
+    TEST(extensionfield2.test2 == 54321)
+    
+    return 0;
+}
+
diff --git a/third_party/nanopb/tests/extensions/encode_extensions.c b/third_party/nanopb/tests/extensions/encode_extensions.c
new file mode 100644
index 0000000..0074582
--- /dev/null
+++ b/third_party/nanopb/tests/extensions/encode_extensions.c
@@ -0,0 +1,54 @@
+/* Tests extension fields.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_encode.h>
+#include "alltypes.pb.h"
+#include "extensions.pb.h"
+#include "test_helpers.h"
+
+int main(int argc, char **argv)
+{
+    uint8_t buffer[1024];
+    pb_ostream_t stream;
+
+    AllTypes alltypes = {0};
+    int32_t extensionfield1 = 12345;
+    pb_extension_t ext1;
+    ExtensionMessage extensionfield2 = {"test", 54321};
+    pb_extension_t ext2;
+
+    /* Set up the extensions */
+    alltypes.extensions = &ext1;
+
+    ext1.type = &AllTypes_extensionfield1;
+    ext1.dest = &extensionfield1;
+    ext1.next = &ext2;
+    
+    ext2.type = &ExtensionMessage_AllTypes_extensionfield2;
+    ext2.dest = &extensionfield2;
+    ext2.next = NULL;
+
+    /* Set up the output stream */
+    stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+    
+    /* Now encode the message and check if we succeeded. */
+    if (pb_encode(&stream, AllTypes_fields, &alltypes))
+    {
+        SET_BINARY_MODE(stdout);
+        fwrite(buffer, 1, stream.bytes_written, stdout);
+        return 0; /* Success */
+    }
+    else
+    {
+        fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
+        return 1; /* Failure */
+    }
+    
+    /* Check that the field tags are properly generated */
+    (void)AllTypes_extensionfield1_tag;
+    (void)ExtensionMessage_AllTypes_extensionfield2_tag;
+}
+
diff --git a/third_party/nanopb/tests/extensions/extensions.options b/third_party/nanopb/tests/extensions/extensions.options
new file mode 100644
index 0000000..a5cd61d
--- /dev/null
+++ b/third_party/nanopb/tests/extensions/extensions.options
@@ -0,0 +1 @@
+* max_size:16
diff --git a/third_party/nanopb/tests/extensions/extensions.proto b/third_party/nanopb/tests/extensions/extensions.proto
new file mode 100644
index 0000000..fcd5b43
--- /dev/null
+++ b/third_party/nanopb/tests/extensions/extensions.proto
@@ -0,0 +1,19 @@
+syntax = "proto2";
+
+import 'alltypes.proto';
+
+extend AllTypes {
+    optional int32 AllTypes_extensionfield1 = 255 [default = 5];
+}
+
+message ExtensionMessage {
+    extend AllTypes {
+        optional ExtensionMessage AllTypes_extensionfield2 = 254;
+        // required ExtensionMessage AllTypes_extensionfield3 = 253; // No longer allowed by protobuf 3
+        repeated ExtensionMessage AllTypes_extensionfield4 = 252;
+    }
+    
+    required string test1 = 1;
+    required int32 test2 = 2;
+}
+
diff --git a/third_party/nanopb/tests/extra_fields/SConscript b/third_party/nanopb/tests/extra_fields/SConscript
new file mode 100644
index 0000000..75ac5c5
--- /dev/null
+++ b/third_party/nanopb/tests/extra_fields/SConscript
@@ -0,0 +1,16 @@
+# Test that the decoder properly handles unknown fields in the input.
+
+Import("env")
+
+dec = env.GetBuildPath('$BUILD/basic_buffer/${PROGPREFIX}decode_buffer${PROGSUFFIX}')
+env.RunTest('person_with_extra_field.output', [dec, "person_with_extra_field.pb"])
+env.Compare(["person_with_extra_field.output", "person_with_extra_field.expected"])
+
+dec = env.GetBuildPath('$BUILD/basic_stream/${PROGPREFIX}decode_stream${PROGSUFFIX}')
+env.RunTest('person_with_extra_field_stream.output', [dec, "person_with_extra_field.pb"])
+env.Compare(["person_with_extra_field_stream.output", "person_with_extra_field.expected"])
+
+# This uses the backwards compatibility alltypes test, so that
+# alltypes_with_extra_fields.pb doesn't have to be remade so often.
+dec2 = env.GetBuildPath('$BUILD/backwards_compatibility/${PROGPREFIX}decode_legacy${PROGSUFFIX}')
+env.RunTest('alltypes_with_extra_fields.output', [dec2, 'alltypes_with_extra_fields.pb'])
diff --git a/third_party/nanopb/tests/extra_fields/person_with_extra_field.expected b/third_party/nanopb/tests/extra_fields/person_with_extra_field.expected
new file mode 100644
index 0000000..da9c32d
--- /dev/null
+++ b/third_party/nanopb/tests/extra_fields/person_with_extra_field.expected
@@ -0,0 +1,14 @@
+name: "Test Person 99"
+id: 99
+email: "test@person.com"
+phone {
+  number: "555-12345678"
+  type: MOBILE
+}
+phone {
+  number: "99-2342"
+}
+phone {
+  number: "1234-5678"
+  type: WORK
+}
diff --git a/third_party/nanopb/tests/field_size_16/SConscript b/third_party/nanopb/tests/field_size_16/SConscript
new file mode 100644
index 0000000..ffb29c4
--- /dev/null
+++ b/third_party/nanopb/tests/field_size_16/SConscript
@@ -0,0 +1,29 @@
+# Run the alltypes test case, but compile with PB_FIELD_16BIT=1.
+# Also the .proto file has been modified to have high indexes.
+
+Import("env")
+
+# Take copy of the files for custom build.
+c = Copy("$TARGET", "$SOURCE")
+env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c)
+env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
+
+env.NanopbProto(["alltypes", "alltypes.options"])
+
+# Define the compilation options
+opts = env.Clone()
+opts.Append(CPPDEFINES = {'PB_FIELD_16BIT': 1})
+
+# Build new version of core
+strict = opts.Clone()
+strict.Append(CFLAGS = strict['CORECFLAGS'])
+strict.Object("pb_decode_fields16.o", "$NANOPB/pb_decode.c")
+strict.Object("pb_encode_fields16.o", "$NANOPB/pb_encode.c")
+strict.Object("pb_common_fields16.o", "$NANOPB/pb_common.c")
+
+# Now build and run the test normally.
+enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_fields16.o", "pb_common_fields16.o"])
+dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_fields16.o", "pb_common_fields16.o"])
+
+env.RunTest(enc)
+env.RunTest([dec, "encode_alltypes.output"])
diff --git a/third_party/nanopb/tests/field_size_16/alltypes.options b/third_party/nanopb/tests/field_size_16/alltypes.options
new file mode 100644
index 0000000..b31e3cf
--- /dev/null
+++ b/third_party/nanopb/tests/field_size_16/alltypes.options
@@ -0,0 +1,3 @@
+* max_size:16
+* max_count:5
+
diff --git a/third_party/nanopb/tests/field_size_16/alltypes.proto b/third_party/nanopb/tests/field_size_16/alltypes.proto
new file mode 100644
index 0000000..ba1ec38
--- /dev/null
+++ b/third_party/nanopb/tests/field_size_16/alltypes.proto
@@ -0,0 +1,121 @@
+syntax = "proto2";
+
+message SubMessage {
+    required string substuff1 = 1 [default = "1"];
+    required int32 substuff2 = 2 [default = 2];
+    optional fixed32 substuff3 = 65535 [default = 3];
+}
+
+message EmptyMessage {
+
+}
+
+enum HugeEnum {
+    Negative = -2147483647; /* protoc doesn't accept -2147483648 here */
+    Positive =  2147483647;
+}
+
+message Limits {
+    required int32      int32_min  =  1;
+    required int32      int32_max  =  2;
+    required uint32     uint32_min =  3;
+    required uint32     uint32_max =  4;
+    required int64      int64_min  =  5;
+    required int64      int64_max  =  6;
+    required uint64     uint64_min =  7;
+    required uint64     uint64_max =  8;
+    required HugeEnum   enum_min   =  9;
+    required HugeEnum   enum_max   = 10;
+}
+
+enum MyEnum {
+    Zero = 0;
+    First = 1;
+    Second = 2;
+    Truth = 42;
+}
+
+message AllTypes {
+    required int32      req_int32   = 1;
+    required int64      req_int64   = 2;
+    required uint32     req_uint32  = 3;
+    required uint64     req_uint64  = 4;
+    required sint32     req_sint32  = 5;
+    required sint64     req_sint64  = 6;
+    required bool       req_bool    = 7;
+    
+    required fixed32    req_fixed32 = 8;
+    required sfixed32   req_sfixed32= 9;
+    required float      req_float   = 10;
+    
+    required fixed64    req_fixed64 = 11;
+    required sfixed64   req_sfixed64= 12;
+    required double     req_double  = 13;
+    
+    required string     req_string  = 14;
+    required bytes      req_bytes   = 15;
+    required SubMessage req_submsg  = 16;
+    required MyEnum     req_enum    = 17;
+    required EmptyMessage req_emptymsg = 18;
+    
+    
+    repeated int32      rep_int32   = 21;
+    repeated int64      rep_int64   = 22;
+    repeated uint32     rep_uint32  = 23;
+    repeated uint64     rep_uint64  = 24;
+    repeated sint32     rep_sint32  = 25;
+    repeated sint64     rep_sint64  = 26;
+    repeated bool       rep_bool    = 27;
+    
+    repeated fixed32    rep_fixed32 = 28;
+    repeated sfixed32   rep_sfixed32= 29;
+    repeated float      rep_float   = 30;
+    
+    repeated fixed64    rep_fixed64 = 10031;
+    repeated sfixed64   rep_sfixed64= 10032;
+    repeated double     rep_double  = 10033;
+    
+    repeated string     rep_string  = 10034;
+    repeated bytes      rep_bytes   = 10035;
+    repeated SubMessage rep_submsg  = 10036;
+    repeated MyEnum     rep_enum    = 10037;
+    repeated EmptyMessage rep_emptymsg = 10038;
+    
+    optional int32      opt_int32   = 10041 [default = 4041];
+    optional int64      opt_int64   = 10042 [default = 4042];
+    optional uint32     opt_uint32  = 10043 [default = 4043];
+    optional uint64     opt_uint64  = 10044 [default = 4044];
+    optional sint32     opt_sint32  = 10045 [default = 4045];
+    optional sint64     opt_sint64  = 10046 [default = 4046];
+    optional bool       opt_bool    = 10047 [default = false];
+    
+    optional fixed32    opt_fixed32 = 10048 [default = 4048];
+    optional sfixed32   opt_sfixed32= 10049 [default = 4049];
+    optional float      opt_float   = 10050 [default = 4050];
+    
+    optional fixed64    opt_fixed64 = 10051 [default = 4051];
+    optional sfixed64   opt_sfixed64= 10052 [default = 4052];
+    optional double     opt_double  = 10053 [default = 4053];
+    
+    optional string     opt_string  = 10054 [default = "4054"];
+    optional bytes      opt_bytes   = 10055 [default = "4055"];
+    optional SubMessage opt_submsg  = 10056;
+    optional MyEnum     opt_enum    = 10057 [default = Second];
+    optional EmptyMessage opt_emptymsg = 10058;
+
+    oneof oneof
+    {
+        SubMessage oneof_msg1 = 10059;
+        EmptyMessage oneof_msg2 = 10060;
+    }
+
+    // Check that extreme integer values are handled correctly
+    required Limits     req_limits = 98;
+
+    // Just to make sure that the size of the fields has been calculated
+    // properly, i.e. otherwise a bug in last field might not be detected.
+    required int32      end = 10099;
+
+    extensions 200 to 255;
+}
+
diff --git a/third_party/nanopb/tests/field_size_32/SConscript b/third_party/nanopb/tests/field_size_32/SConscript
new file mode 100644
index 0000000..0b8dc0e
--- /dev/null
+++ b/third_party/nanopb/tests/field_size_32/SConscript
@@ -0,0 +1,29 @@
+# Run the alltypes test case, but compile with PB_FIELD_32BIT=1.
+# Also the .proto file has been modified to have high indexes.
+
+Import("env")
+
+# Take copy of the files for custom build.
+c = Copy("$TARGET", "$SOURCE")
+env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c)
+env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
+
+env.NanopbProto(["alltypes", "alltypes.options"])
+
+# Define the compilation options
+opts = env.Clone()
+opts.Append(CPPDEFINES = {'PB_FIELD_32BIT': 1})
+
+# Build new version of core
+strict = opts.Clone()
+strict.Append(CFLAGS = strict['CORECFLAGS'])
+strict.Object("pb_decode_fields32.o", "$NANOPB/pb_decode.c")
+strict.Object("pb_encode_fields32.o", "$NANOPB/pb_encode.c")
+strict.Object("pb_common_fields32.o", "$NANOPB/pb_common.c")
+
+# Now build and run the test normally.
+enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_fields32.o", "pb_common_fields32.o"])
+dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_fields32.o", "pb_common_fields32.o"])
+
+env.RunTest(enc)
+env.RunTest([dec, "encode_alltypes.output"])
diff --git a/third_party/nanopb/tests/field_size_32/alltypes.options b/third_party/nanopb/tests/field_size_32/alltypes.options
new file mode 100644
index 0000000..b31e3cf
--- /dev/null
+++ b/third_party/nanopb/tests/field_size_32/alltypes.options
@@ -0,0 +1,3 @@
+* max_size:16
+* max_count:5
+
diff --git a/third_party/nanopb/tests/field_size_32/alltypes.proto b/third_party/nanopb/tests/field_size_32/alltypes.proto
new file mode 100644
index 0000000..02ee1a6
--- /dev/null
+++ b/third_party/nanopb/tests/field_size_32/alltypes.proto
@@ -0,0 +1,121 @@
+syntax = "proto2";
+
+message SubMessage {
+    required string substuff1 = 1 [default = "1"];
+    required int32 substuff2 = 2 [default = 2];
+    optional fixed32 substuff3 = 12365535 [default = 3];
+}
+
+message EmptyMessage {
+
+}
+
+enum HugeEnum {
+    Negative = -2147483647; /* protoc doesn't accept -2147483648 here */
+    Positive =  2147483647;
+}
+
+message Limits {
+    required int32      int32_min  =  1;
+    required int32      int32_max  =  2;
+    required uint32     uint32_min =  3;
+    required uint32     uint32_max =  4;
+    required int64      int64_min  =  5;
+    required int64      int64_max  =  6;
+    required uint64     uint64_min =  7;
+    required uint64     uint64_max =  8;
+    required HugeEnum   enum_min   =  9;
+    required HugeEnum   enum_max   = 10;
+}
+
+enum MyEnum {
+    Zero = 0;
+    First = 1;
+    Second = 2;
+    Truth = 42;
+}
+
+message AllTypes {
+    required int32      req_int32   = 1;
+    required int64      req_int64   = 2;
+    required uint32     req_uint32  = 3;
+    required uint64     req_uint64  = 4;
+    required sint32     req_sint32  = 5;
+    required sint64     req_sint64  = 6;
+    required bool       req_bool    = 7;
+    
+    required fixed32    req_fixed32 = 8;
+    required sfixed32   req_sfixed32= 9;
+    required float      req_float   = 10;
+    
+    required fixed64    req_fixed64 = 11;
+    required sfixed64   req_sfixed64= 12;
+    required double     req_double  = 13;
+    
+    required string     req_string  = 14;
+    required bytes      req_bytes   = 15;
+    required SubMessage req_submsg  = 16;
+    required MyEnum     req_enum    = 17;
+    required EmptyMessage req_emptymsg = 18;
+    
+    
+    repeated int32      rep_int32   = 21;
+    repeated int64      rep_int64   = 22;
+    repeated uint32     rep_uint32  = 23;
+    repeated uint64     rep_uint64  = 24;
+    repeated sint32     rep_sint32  = 25;
+    repeated sint64     rep_sint64  = 26;
+    repeated bool       rep_bool    = 27;
+    
+    repeated fixed32    rep_fixed32 = 28;
+    repeated sfixed32   rep_sfixed32= 29;
+    repeated float      rep_float   = 30;
+    
+    repeated fixed64    rep_fixed64 = 10031;
+    repeated sfixed64   rep_sfixed64= 10032;
+    repeated double     rep_double  = 10033;
+    
+    repeated string     rep_string  = 10034;
+    repeated bytes      rep_bytes   = 10035;
+    repeated SubMessage rep_submsg  = 10036;
+    repeated MyEnum     rep_enum    = 10037;
+    repeated EmptyMessage rep_emptymsg = 10038;
+    
+    optional int32      opt_int32   = 10041 [default = 4041];
+    optional int64      opt_int64   = 10042 [default = 4042];
+    optional uint32     opt_uint32  = 10043 [default = 4043];
+    optional uint64     opt_uint64  = 10044 [default = 4044];
+    optional sint32     opt_sint32  = 10045 [default = 4045];
+    optional sint64     opt_sint64  = 10046 [default = 4046];
+    optional bool       opt_bool    = 10047 [default = false];
+    
+    optional fixed32    opt_fixed32 = 10048 [default = 4048];
+    optional sfixed32   opt_sfixed32= 10049 [default = 4049];
+    optional float      opt_float   = 10050 [default = 4050];
+    
+    optional fixed64    opt_fixed64 = 10051 [default = 4051];
+    optional sfixed64   opt_sfixed64= 10052 [default = 4052];
+    optional double     opt_double  = 10053 [default = 4053];
+    
+    optional string     opt_string  = 10054 [default = "4054"];
+    optional bytes      opt_bytes   = 10055 [default = "4055"];
+    optional SubMessage opt_submsg  = 10056;
+    optional MyEnum     opt_enum    = 10057 [default = Second];
+    optional EmptyMessage opt_emptymsg = 10058;
+
+    oneof oneof
+    {
+        SubMessage oneof_msg1 = 10059;
+        EmptyMessage oneof_msg2 = 10060;
+    }
+
+    // Check that extreme integer values are handled correctly
+    required Limits     req_limits = 98;
+
+    // Just to make sure that the size of the fields has been calculated
+    // properly, i.e. otherwise a bug in last field might not be detected.
+    required int32      end = 13432099;
+
+    extensions 200 to 255;
+}
+
diff --git a/third_party/nanopb/tests/fuzztest/SConscript b/third_party/nanopb/tests/fuzztest/SConscript
new file mode 100644
index 0000000..d2fb689
--- /dev/null
+++ b/third_party/nanopb/tests/fuzztest/SConscript
@@ -0,0 +1,43 @@
+# Run a fuzz test to verify robustness against corrupted/malicious data.
+
+Import("env", "malloc_env")
+
+def set_pkgname(src, dst, pkgname):
+    data = open(str(src)).read()
+    placeholder = '// package name placeholder'
+    assert placeholder in data
+    data = data.replace(placeholder, 'package %s;' % pkgname)
+    open(str(dst), 'w').write(data)
+
+# We want both pointer and static versions of the AllTypes message
+# Prefix them with package name.
+env.Command("alltypes_static.proto", "#alltypes/alltypes.proto",
+            lambda target, source, env: set_pkgname(source[0], target[0], 'alltypes_static'))
+env.Command("alltypes_pointer.proto", "#alltypes/alltypes.proto",
+            lambda target, source, env: set_pkgname(source[0], target[0], 'alltypes_pointer'))
+
+p1 = env.NanopbProto(["alltypes_pointer", "alltypes_pointer.options"])
+p2 = env.NanopbProto(["alltypes_static", "alltypes_static.options"])
+fuzz = malloc_env.Program(["fuzztest.c",
+                    "alltypes_pointer.pb.c",
+                    "alltypes_static.pb.c",
+                    "$COMMON/pb_encode_with_malloc.o",
+                    "$COMMON/pb_decode_with_malloc.o",
+                    "$COMMON/pb_common_with_malloc.o",
+                    "$COMMON/malloc_wrappers.o"])
+
+env.RunTest(fuzz)
+
+fuzzstub = malloc_env.Program(["fuzzstub.c",
+                    "alltypes_pointer.pb.c",
+                    "alltypes_static.pb.c",
+                    "$COMMON/pb_encode_with_malloc.o",
+                    "$COMMON/pb_decode_with_malloc.o",
+                    "$COMMON/pb_common_with_malloc.o",
+                    "$COMMON/malloc_wrappers.o"])
+
+generate_message = malloc_env.Program(["generate_message.c",
+                    "alltypes_static.pb.c",
+                    "$COMMON/pb_encode.o",
+                    "$COMMON/pb_common.o"])
+
diff --git a/third_party/nanopb/tests/fuzztest/alltypes_pointer.options b/third_party/nanopb/tests/fuzztest/alltypes_pointer.options
new file mode 100644
index 0000000..52abeb7
--- /dev/null
+++ b/third_party/nanopb/tests/fuzztest/alltypes_pointer.options
@@ -0,0 +1,3 @@
+# Generate all fields as pointers.
+* type:FT_POINTER
+
diff --git a/third_party/nanopb/tests/fuzztest/alltypes_static.options b/third_party/nanopb/tests/fuzztest/alltypes_static.options
new file mode 100644
index 0000000..1c10637
--- /dev/null
+++ b/third_party/nanopb/tests/fuzztest/alltypes_static.options
@@ -0,0 +1,3 @@
+* max_size:32
+* max_count:8
+*.extensions type:FT_IGNORE
diff --git a/third_party/nanopb/tests/fuzztest/fuzzstub.c b/third_party/nanopb/tests/fuzztest/fuzzstub.c
new file mode 100644
index 0000000..ec9e2af
--- /dev/null
+++ b/third_party/nanopb/tests/fuzztest/fuzzstub.c
@@ -0,0 +1,189 @@
+/* Fuzz testing for the nanopb core.
+ * This can be used with external fuzzers, e.g. radamsa.
+ * It performs most of the same checks as fuzztest, but does not feature data generation.
+ */
+
+#include <pb_decode.h>
+#include <pb_encode.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <malloc_wrappers.h>
+#include "alltypes_static.pb.h"
+#include "alltypes_pointer.pb.h"
+
+#define BUFSIZE 4096
+
+static bool do_static_decode(uint8_t *buffer, size_t msglen, bool assert_success)
+{
+    pb_istream_t stream;
+    bool status;
+    
+    alltypes_static_AllTypes *msg = malloc_with_check(sizeof(alltypes_static_AllTypes));
+    stream = pb_istream_from_buffer(buffer, msglen);
+    status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg);
+    
+    if (!status && assert_success)
+    {
+        /* Anything that was successfully encoded, should be decodeable.
+         * One exception: strings without null terminator are encoded up
+         * to end of buffer, but refused on decode because the terminator
+         * would not fit. */
+        if (strcmp(stream.errmsg, "string overflow") != 0)
+            assert(status);
+    }
+    
+    free_with_check(msg);
+    return status;
+}
+
+static bool do_pointer_decode(uint8_t *buffer, size_t msglen, bool assert_success)
+{
+    pb_istream_t stream;
+    bool status;
+    alltypes_pointer_AllTypes *msg;
+    
+    msg = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
+    memset(msg, 0, sizeof(alltypes_pointer_AllTypes));
+    stream = pb_istream_from_buffer(buffer, msglen);
+
+    assert(get_alloc_count() == 0);
+    status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg);
+    
+    if (assert_success)
+        assert(status);
+    
+    pb_release(alltypes_pointer_AllTypes_fields, msg);    
+    assert(get_alloc_count() == 0);
+    
+    free_with_check(msg);
+
+    return status;
+}
+
+/* Do a decode -> encode -> decode -> encode roundtrip */
+static void do_static_roundtrip(uint8_t *buffer, size_t msglen)
+{
+    bool status;
+    uint8_t *buf2 = malloc_with_check(BUFSIZE);
+    uint8_t *buf3 = malloc_with_check(BUFSIZE);
+    size_t msglen2, msglen3;
+    alltypes_static_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_static_AllTypes));
+    alltypes_static_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_static_AllTypes));
+    memset(msg1, 0, sizeof(alltypes_static_AllTypes));
+    memset(msg2, 0, sizeof(alltypes_static_AllTypes));
+    
+    {
+        pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
+        status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg1);
+        assert(status);
+    }
+    
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE);
+        status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg1);
+        assert(status);
+        msglen2 = stream.bytes_written;
+    }
+    
+    {
+        pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2);
+        status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg2);
+        assert(status);
+    }
+    
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE);
+        status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg2);
+        assert(status);
+        msglen3 = stream.bytes_written;
+    }
+    
+    assert(msglen2 == msglen3);
+    assert(memcmp(buf2, buf3, msglen2) == 0);
+    
+    free_with_check(msg1);
+    free_with_check(msg2);
+    free_with_check(buf2);
+    free_with_check(buf3);
+}
+
+/* Do decode -> encode -> decode -> encode roundtrip */
+static void do_pointer_roundtrip(uint8_t *buffer, size_t msglen)
+{
+    bool status;
+    uint8_t *buf2 = malloc_with_check(BUFSIZE);
+    uint8_t *buf3 = malloc_with_check(BUFSIZE);
+    size_t msglen2, msglen3;
+    alltypes_pointer_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
+    alltypes_pointer_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
+    memset(msg1, 0, sizeof(alltypes_pointer_AllTypes));
+    memset(msg2, 0, sizeof(alltypes_pointer_AllTypes));
+    
+    {
+        pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
+        status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg1);
+        assert(status);
+    }
+    
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE);
+        status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg1);
+        assert(status);
+        msglen2 = stream.bytes_written;
+    }
+    
+    {
+        pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2);
+        status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg2);
+        assert(status);
+    }
+    
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE);
+        status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg2);
+        assert(status);
+        msglen3 = stream.bytes_written;
+    }
+    
+    assert(msglen2 == msglen3);
+    assert(memcmp(buf2, buf3, msglen2) == 0);
+    
+    pb_release(alltypes_pointer_AllTypes_fields, msg1);
+    pb_release(alltypes_pointer_AllTypes_fields, msg2);
+    free_with_check(msg1);
+    free_with_check(msg2);
+    free_with_check(buf2);
+    free_with_check(buf3);
+}
+
+static void run_iteration()
+{
+    uint8_t *buffer = malloc_with_check(BUFSIZE);
+    size_t msglen;
+    bool status;
+    
+    msglen = fread(buffer, 1, BUFSIZE, stdin);
+
+    status = do_static_decode(buffer, msglen, false);
+    
+    if (status)
+        do_static_roundtrip(buffer, msglen);
+    
+    status = do_pointer_decode(buffer, msglen, false);
+    
+    if (status)
+        do_pointer_roundtrip(buffer, msglen);
+    
+    free_with_check(buffer);
+}
+
+int main(int argc, char **argv)
+{
+    run_iteration();
+    
+    return 0;
+}
+
diff --git a/third_party/nanopb/tests/fuzztest/fuzztest.c b/third_party/nanopb/tests/fuzztest/fuzztest.c
new file mode 100644
index 0000000..ee851ec
--- /dev/null
+++ b/third_party/nanopb/tests/fuzztest/fuzztest.c
@@ -0,0 +1,432 @@
+/* Fuzz testing for the nanopb core.
+ * Attempts to verify all the properties defined in the security model document.
+ */
+
+#include <pb_decode.h>
+#include <pb_encode.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <malloc_wrappers.h>
+#include "alltypes_static.pb.h"
+#include "alltypes_pointer.pb.h"
+
+static uint64_t random_seed;
+
+/* Uses xorshift64 here instead of rand() for both speed and
+ * reproducibility across platforms. */
+static uint32_t rand_word()
+{
+    random_seed ^= random_seed >> 12;
+    random_seed ^= random_seed << 25;
+    random_seed ^= random_seed >> 27;
+    return random_seed * 2685821657736338717ULL;
+}
+
+/* Get a random integer in range, with approximately flat distribution. */
+static int rand_int(int min, int max)
+{
+    return rand_word() % (max + 1 - min) + min;
+}
+
+static bool rand_bool()
+{
+    return rand_word() & 1;
+}
+
+/* Get a random byte, with skewed distribution.
+ * Important corner cases like 0xFF, 0x00 and 0xFE occur more
+ * often than other values. */
+static uint8_t rand_byte()
+{
+    uint32_t w = rand_word();
+    uint8_t b = w & 0xFF;
+    if (w & 0x100000)
+        b >>= (w >> 8) & 7;
+    if (w & 0x200000)
+        b <<= (w >> 12) & 7;
+    if (w & 0x400000)
+        b ^= 0xFF;
+    return b;
+}
+
+/* Get a random length, with skewed distribution.
+ * Favors the shorter lengths, but always atleast 1. */
+static size_t rand_len(size_t max)
+{
+    uint32_t w = rand_word();
+    size_t s;
+    if (w & 0x800000)
+        w &= 3;
+    else if (w & 0x400000)
+        w &= 15;
+    else if (w & 0x200000)
+        w &= 255;
+
+    s = (w % max);
+    if (s == 0)
+        s = 1;
+    
+    return s;
+}
+
+/* Fills a buffer with random data with skewed distribution. */
+static void rand_fill(uint8_t *buf, size_t count)
+{
+    while (count--)
+        *buf++ = rand_byte();
+}
+
+/* Fill with random protobuf-like data */
+static size_t rand_fill_protobuf(uint8_t *buf, size_t min_bytes, size_t max_bytes, int min_tag)
+{
+    pb_ostream_t stream = pb_ostream_from_buffer(buf, max_bytes);
+
+    while(stream.bytes_written < min_bytes)
+    {
+        pb_wire_type_t wt = rand_int(0, 3);
+        if (wt == 3) wt = 5; /* Gap in values */
+        
+        if (!pb_encode_tag(&stream, wt, rand_int(min_tag, min_tag + 512)))
+            break;
+    
+        if (wt == PB_WT_VARINT)
+        {
+            uint64_t value;
+            rand_fill((uint8_t*)&value, sizeof(value));
+            pb_encode_varint(&stream, value);
+        }
+        else if (wt == PB_WT_64BIT)
+        {
+            uint64_t value;
+            rand_fill((uint8_t*)&value, sizeof(value));
+            pb_encode_fixed64(&stream, &value);
+        }
+        else if (wt == PB_WT_32BIT)
+        {
+            uint32_t value;
+            rand_fill((uint8_t*)&value, sizeof(value));
+            pb_encode_fixed32(&stream, &value);
+        }
+        else if (wt == PB_WT_STRING)
+        {
+            size_t len;
+            uint8_t *buf;
+            
+            if (min_bytes > stream.bytes_written)
+                len = rand_len(min_bytes - stream.bytes_written);
+            else
+                len = 0;
+            
+            buf = malloc(len);
+            pb_encode_varint(&stream, len);
+            rand_fill(buf, len);
+            pb_write(&stream, buf, len);
+            free(buf);
+        }
+    }
+    
+    return stream.bytes_written;
+}
+
+/* Given a buffer of data, mess it up a bit */
+static void rand_mess(uint8_t *buf, size_t count)
+{
+    int m = rand_int(0, 3);
+    
+    if (m == 0)
+    {
+        /* Replace random substring */
+        int s = rand_int(0, count - 1);
+        int l = rand_len(count - s);
+        rand_fill(buf + s, l);
+    }
+    else if (m == 1)
+    {
+        /* Swap random bytes */
+        int a = rand_int(0, count - 1);
+        int b = rand_int(0, count - 1);
+        int x = buf[a];
+        buf[a] = buf[b];
+        buf[b] = x;
+    }
+    else if (m == 2)
+    {
+        /* Duplicate substring */
+        int s = rand_int(0, count - 2);
+        int l = rand_len((count - s) / 2);
+        memcpy(buf + s + l, buf + s, l);
+    }
+    else if (m == 3)
+    {
+        /* Add random protobuf noise */
+        int s = rand_int(0, count - 1);
+        int l = rand_len(count - s);
+        rand_fill_protobuf(buf + s, l, count - s, 1);
+    }
+}
+
+/* Some default data to put in the message */
+static const alltypes_static_AllTypes initval = alltypes_static_AllTypes_init_default;
+
+#define BUFSIZE 4096
+
+static bool do_static_encode(uint8_t *buffer, size_t *msglen)
+{
+    pb_ostream_t stream;
+    bool status;
+
+    /* Allocate a message and fill it with defaults */
+    alltypes_static_AllTypes *msg = malloc_with_check(sizeof(alltypes_static_AllTypes));
+    memcpy(msg, &initval, sizeof(initval));
+
+    /* Apply randomness to the data before encoding */
+    while (rand_int(0, 7))
+        rand_mess((uint8_t*)msg, sizeof(alltypes_static_AllTypes));
+
+    stream = pb_ostream_from_buffer(buffer, BUFSIZE);
+    status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg);
+    assert(stream.bytes_written <= BUFSIZE);
+    assert(stream.bytes_written <= alltypes_static_AllTypes_size);
+    
+    *msglen = stream.bytes_written;
+    pb_release(alltypes_static_AllTypes_fields, msg);
+    free_with_check(msg);
+    
+    return status;
+}
+
+/* Append or prepend protobuf noise */
+static void do_protobuf_noise(uint8_t *buffer, size_t *msglen)
+{
+    int m = rand_int(0, 2);
+    size_t max_size = BUFSIZE - 32 - *msglen;
+    if (m == 1)
+    {
+        /* Prepend */
+        uint8_t *tmp = malloc_with_check(BUFSIZE);
+        size_t s = rand_fill_protobuf(tmp, rand_len(max_size), BUFSIZE - *msglen, 512);
+        memmove(buffer + s, buffer, *msglen);
+        memcpy(buffer, tmp, s);
+        free_with_check(tmp);
+        *msglen += s;
+    }
+    else if (m == 2)
+    {
+        /* Append */
+        size_t s = rand_fill_protobuf(buffer + *msglen, rand_len(max_size), BUFSIZE - *msglen, 512);
+        *msglen += s;
+    }
+}
+
+static bool do_static_decode(uint8_t *buffer, size_t msglen, bool assert_success)
+{
+    pb_istream_t stream;
+    bool status;
+    
+    alltypes_static_AllTypes *msg = malloc_with_check(sizeof(alltypes_static_AllTypes));
+    rand_fill((uint8_t*)msg, sizeof(alltypes_static_AllTypes));
+    stream = pb_istream_from_buffer(buffer, msglen);
+    status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg);
+    
+    if (!status && assert_success)
+    {
+        /* Anything that was successfully encoded, should be decodeable.
+         * One exception: strings without null terminator are encoded up
+         * to end of buffer, but refused on decode because the terminator
+         * would not fit. */
+        if (strcmp(stream.errmsg, "string overflow") != 0)
+            assert(status);
+    }
+    
+    free_with_check(msg);
+    return status;
+}
+
+static bool do_pointer_decode(uint8_t *buffer, size_t msglen, bool assert_success)
+{
+    pb_istream_t stream;
+    bool status;
+    alltypes_pointer_AllTypes *msg;
+    
+    msg = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
+    memset(msg, 0, sizeof(alltypes_pointer_AllTypes));
+    stream = pb_istream_from_buffer(buffer, msglen);
+
+    assert(get_alloc_count() == 0);
+    status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg);
+    
+    if (assert_success)
+        assert(status);
+    
+    pb_release(alltypes_pointer_AllTypes_fields, msg);    
+    assert(get_alloc_count() == 0);
+    
+    free_with_check(msg);
+
+    return status;
+}
+
+/* Do a decode -> encode -> decode -> encode roundtrip */
+static void do_static_roundtrip(uint8_t *buffer, size_t msglen)
+{
+    bool status;
+    uint8_t *buf2 = malloc_with_check(BUFSIZE);
+    uint8_t *buf3 = malloc_with_check(BUFSIZE);
+    size_t msglen2, msglen3;
+    alltypes_static_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_static_AllTypes));
+    alltypes_static_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_static_AllTypes));
+    memset(msg1, 0, sizeof(alltypes_static_AllTypes));
+    memset(msg2, 0, sizeof(alltypes_static_AllTypes));
+    
+    {
+        pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
+        status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg1);
+        assert(status);
+    }
+    
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE);
+        status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg1);
+        assert(status);
+        msglen2 = stream.bytes_written;
+    }
+    
+    {
+        pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2);
+        status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg2);
+        assert(status);
+    }
+    
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE);
+        status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg2);
+        assert(status);
+        msglen3 = stream.bytes_written;
+    }
+    
+    assert(msglen2 == msglen3);
+    assert(memcmp(buf2, buf3, msglen2) == 0);
+    
+    free_with_check(msg1);
+    free_with_check(msg2);
+    free_with_check(buf2);
+    free_with_check(buf3);
+}
+
+/* Do decode -> encode -> decode -> encode roundtrip */
+static void do_pointer_roundtrip(uint8_t *buffer, size_t msglen)
+{
+    bool status;
+    uint8_t *buf2 = malloc_with_check(BUFSIZE);
+    uint8_t *buf3 = malloc_with_check(BUFSIZE);
+    size_t msglen2, msglen3;
+    alltypes_pointer_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
+    alltypes_pointer_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
+    memset(msg1, 0, sizeof(alltypes_pointer_AllTypes));
+    memset(msg2, 0, sizeof(alltypes_pointer_AllTypes));
+    
+    {
+        pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
+        status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg1);
+        assert(status);
+    }
+    
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE);
+        status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg1);
+        assert(status);
+        msglen2 = stream.bytes_written;
+    }
+    
+    {
+        pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2);
+        status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg2);
+        assert(status);
+    }
+    
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE);
+        status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg2);
+        assert(status);
+        msglen3 = stream.bytes_written;
+    }
+    
+    assert(msglen2 == msglen3);
+    assert(memcmp(buf2, buf3, msglen2) == 0);
+    
+    pb_release(alltypes_pointer_AllTypes_fields, msg1);
+    pb_release(alltypes_pointer_AllTypes_fields, msg2);
+    free_with_check(msg1);
+    free_with_check(msg2);
+    free_with_check(buf2);
+    free_with_check(buf3);
+}
+
+static void run_iteration()
+{
+    uint8_t *buffer = malloc_with_check(BUFSIZE);
+    size_t msglen;
+    bool status;
+    
+    rand_fill(buffer, BUFSIZE);
+
+    if (do_static_encode(buffer, &msglen))
+    {
+        do_protobuf_noise(buffer, &msglen);
+    
+        status = do_static_decode(buffer, msglen, true);
+        
+        if (status)
+            do_static_roundtrip(buffer, msglen);
+        
+        status = do_pointer_decode(buffer, msglen, true);
+        
+        if (status)
+            do_pointer_roundtrip(buffer, msglen);
+        
+        /* Apply randomness to the encoded data */
+        while (rand_bool())
+            rand_mess(buffer, BUFSIZE);
+        
+        /* Apply randomness to encoded data length */
+        if (rand_bool())
+            msglen = rand_int(0, BUFSIZE);
+        
+        status = do_static_decode(buffer, msglen, false);
+        do_pointer_decode(buffer, msglen, status);
+        
+        if (status)
+        {
+            do_static_roundtrip(buffer, msglen);
+            do_pointer_roundtrip(buffer, msglen);
+        }
+    }
+    
+    free_with_check(buffer);
+}
+
+int main(int argc, char **argv)
+{
+    int i;
+    if (argc > 1)
+    {
+        random_seed = atol(argv[1]);
+    }
+    else
+    {
+        random_seed = time(NULL);
+    }
+    
+    fprintf(stderr, "Random seed: %llu\n", (long long unsigned)random_seed);
+    
+    for (i = 0; i < 10000; i++)
+    {
+        run_iteration();
+    }
+    
+    return 0;
+}
+
diff --git a/third_party/nanopb/tests/fuzztest/generate_message.c b/third_party/nanopb/tests/fuzztest/generate_message.c
new file mode 100644
index 0000000..6e49299
--- /dev/null
+++ b/third_party/nanopb/tests/fuzztest/generate_message.c
@@ -0,0 +1,101 @@
+/* Generates a random, valid protobuf message. Useful to seed
+ * external fuzzers such as afl-fuzz.
+ */
+
+#include <pb_encode.h>
+#include <pb_common.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include "alltypes_static.pb.h"
+
+static uint64_t random_seed;
+
+/* Uses xorshift64 here instead of rand() for both speed and
+ * reproducibility across platforms. */
+static uint32_t rand_word()
+{
+    random_seed ^= random_seed >> 12;
+    random_seed ^= random_seed << 25;
+    random_seed ^= random_seed >> 27;
+    return random_seed * 2685821657736338717ULL;
+}
+
+/* Fills a buffer with random data. */
+static void rand_fill(uint8_t *buf, size_t count)
+{
+    while (count--)
+    {
+        *buf++ = rand_word() & 0xff;
+    }
+}
+
+/* Check that size/count fields do not exceed their max size.
+ * Otherwise we would have to loop pretty long in generate_message().
+ * Note that there may still be a few encoding errors from submessages.
+ */
+static void limit_sizes(alltypes_static_AllTypes *msg)
+{
+    pb_field_iter_t iter;
+    pb_field_iter_begin(&iter, alltypes_static_AllTypes_fields, msg);
+    while (pb_field_iter_next(&iter))
+    {
+        if (PB_LTYPE(iter.pos->type) == PB_LTYPE_BYTES)
+        {
+            ((pb_bytes_array_t*)iter.pData)->size %= iter.pos->data_size - PB_BYTES_ARRAY_T_ALLOCSIZE(0);
+        }
+        
+        if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REPEATED)
+        {
+            *((pb_size_t*)iter.pSize) %= iter.pos->array_size;
+        }
+        
+        if (PB_HTYPE(iter.pos->type) == PB_HTYPE_ONEOF)
+        {
+            /* Set the oneof to this message type with 50% chance. */
+            if (rand_word() & 1)
+            {
+                *((pb_size_t*)iter.pSize) = iter.pos->tag;
+            }
+        }
+    }
+}
+
+static void generate_message()
+{
+    alltypes_static_AllTypes msg;
+    uint8_t buf[8192];
+    pb_ostream_t stream = {0};
+    
+    do {
+        if (stream.errmsg)
+            fprintf(stderr, "Encoder error: %s\n", stream.errmsg);
+        
+        stream = pb_ostream_from_buffer(buf, sizeof(buf));
+        rand_fill((void*)&msg, sizeof(msg));
+        limit_sizes(&msg);
+    } while (!pb_encode(&stream, alltypes_static_AllTypes_fields, &msg));
+    
+    fwrite(buf, 1, stream.bytes_written, stdout);
+}
+
+int main(int argc, char **argv)
+{
+    if (argc > 1)
+    {
+        random_seed = atol(argv[1]);
+    }
+    else
+    {
+        random_seed = time(NULL);
+    }
+    
+    fprintf(stderr, "Random seed: %llu\n", (long long unsigned)random_seed);
+    
+    generate_message();
+    
+    return 0;
+}
+
diff --git a/third_party/nanopb/tests/fuzztest/run_radamsa.sh b/third_party/nanopb/tests/fuzztest/run_radamsa.sh
new file mode 100755
index 0000000..52cd40a
--- /dev/null
+++ b/third_party/nanopb/tests/fuzztest/run_radamsa.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+TMP=`tempfile`
+
+echo $TMP
+while true
+do
+   radamsa sample_data/* > $TMP
+   $1 < $TMP
+   test $? -gt 127 && break
+done
+ 
diff --git a/third_party/nanopb/tests/inline/SConscript b/third_party/nanopb/tests/inline/SConscript
new file mode 100644
index 0000000..34371fd
--- /dev/null
+++ b/third_party/nanopb/tests/inline/SConscript
@@ -0,0 +1,16 @@
+# Test that inlined bytes fields work.
+
+Import("env")
+
+env.NanopbProto("inline")
+env.Object("inline.pb.c")
+
+env.Match(["inline.pb.h", "inline.expected"])
+
+p = env.Program(["inline_unittests.c",
+                 "inline.pb.c",
+                 "$COMMON/pb_encode.o",
+                 "$COMMON/pb_decode.o",
+                 "$COMMON/pb_common.o"])
+
+env.RunTest(p)
diff --git a/third_party/nanopb/tests/inline/inline.expected b/third_party/nanopb/tests/inline/inline.expected
new file mode 100644
index 0000000..593e972
--- /dev/null
+++ b/third_party/nanopb/tests/inline/inline.expected
@@ -0,0 +1,3 @@
+pb_byte_t data\[32\];
+bool has_data;
+pb_byte_t data\[64\];
diff --git a/third_party/nanopb/tests/inline/inline.proto b/third_party/nanopb/tests/inline/inline.proto
new file mode 100644
index 0000000..6e511f0
--- /dev/null
+++ b/third_party/nanopb/tests/inline/inline.proto
@@ -0,0 +1,17 @@
+/* Test nanopb option parsing.
+ * options.expected lists the patterns that are searched for in the output.
+ */
+
+syntax = "proto2";
+
+import "nanopb.proto";
+
+message Message1
+{
+    required bytes data = 1 [(nanopb).type = FT_INLINE, (nanopb).max_size = 32];
+}
+
+message Message2
+{
+    optional bytes data = 1 [(nanopb).type = FT_INLINE, (nanopb).max_size = 64];
+}
diff --git a/third_party/nanopb/tests/inline/inline_unittests.c b/third_party/nanopb/tests/inline/inline_unittests.c
new file mode 100644
index 0000000..b5834c7
--- /dev/null
+++ b/third_party/nanopb/tests/inline/inline_unittests.c
@@ -0,0 +1,73 @@
+#include <stdio.h>
+#include <string.h>
+#include <pb_decode.h>
+#include <pb_encode.h>
+#include "unittests.h"
+#include "inline.pb.h"
+
+int main()
+{
+    int status = 0;
+    int i = 0;
+    COMMENT("Test inline byte fields");
+
+    {
+      Message1 msg1 = Message1_init_zero;
+      TEST(sizeof(msg1.data) == 32);
+    }
+
+    {
+      Message1 msg1 = Message1_init_zero;
+      pb_byte_t msg1_buffer[Message1_size];
+      pb_ostream_t ostream = pb_ostream_from_buffer(msg1_buffer, Message1_size);
+      Message1 msg1_deserialized = Message1_init_zero;
+      pb_istream_t istream = pb_istream_from_buffer(msg1_buffer, Message1_size);
+
+      for (i = 0; i < 32; i++) {
+        msg1.data[i] = i;
+      }
+
+      TEST(pb_encode(&ostream, Message1_fields, &msg1));
+      TEST(ostream.bytes_written == Message1_size);
+
+      TEST(pb_decode(&istream, Message1_fields, &msg1_deserialized));
+
+      TEST(istream.bytes_left == 0);
+      TEST(memcmp(&msg1_deserialized, &msg1, sizeof(msg1)) == 0);
+    }
+
+    {
+      Message2 msg2 = {true, {0}};
+      Message2 msg2_no_data = {false, {1}};
+      pb_byte_t msg2_buffer[Message2_size];
+      pb_ostream_t ostream = pb_ostream_from_buffer(msg2_buffer, Message2_size);
+      Message2 msg2_deserialized = Message2_init_zero;
+      pb_istream_t istream = pb_istream_from_buffer(msg2_buffer, Message2_size);
+
+      for (i = 0; i < 64; i++) {
+        msg2.data[i] = i;
+      }
+
+      TEST(pb_encode(&ostream, Message2_fields, &msg2));
+      TEST(ostream.bytes_written == Message2_size);
+
+      TEST(pb_decode(&istream, Message2_fields, &msg2_deserialized));
+
+      TEST(istream.bytes_left == 0);
+      TEST(memcmp(&msg2_deserialized, &msg2, sizeof(msg2)) == 0);
+      TEST(msg2_deserialized.has_data);
+
+      memset(msg2_buffer, 0, sizeof(msg2_buffer));
+      ostream = pb_ostream_from_buffer(msg2_buffer, Message2_size);
+      TEST(pb_encode(&ostream, Message2_fields, &msg2_no_data));
+      istream = pb_istream_from_buffer(msg2_buffer, Message2_size);
+      TEST(pb_decode(&istream, Message2_fields, &msg2_deserialized));
+      TEST(!msg2_deserialized.has_data);
+      TEST(memcmp(&msg2_deserialized, &msg2, sizeof(msg2)) != 0);
+    }
+
+    if (status != 0)
+        fprintf(stdout, "\n\nSome tests FAILED!\n");
+
+    return status;
+}
diff --git a/third_party/nanopb/tests/intsizes/SConscript b/third_party/nanopb/tests/intsizes/SConscript
new file mode 100644
index 0000000..a90680b
--- /dev/null
+++ b/third_party/nanopb/tests/intsizes/SConscript
@@ -0,0 +1,12 @@
+# Test that the int_size option in .proto works.
+
+Import('env')
+
+env.NanopbProto('intsizes')
+
+p = env.Program(["intsizes_unittests.c",
+                 "intsizes.pb.c",
+                 "$COMMON/pb_encode.o",
+                 "$COMMON/pb_decode.o",
+                 "$COMMON/pb_common.o"])
+env.RunTest(p)
diff --git a/third_party/nanopb/tests/intsizes/intsizes.proto b/third_party/nanopb/tests/intsizes/intsizes.proto
new file mode 100644
index 0000000..91444d4
--- /dev/null
+++ b/third_party/nanopb/tests/intsizes/intsizes.proto
@@ -0,0 +1,41 @@
+/* Test the integer size overriding in nanopb options.
+ * This allows to use 8- and 16-bit integer variables, which are not supported
+ * directly by Google Protobuf.
+ *
+ * The int_size setting will override the number of bits, but keep the type
+ * otherwise. E.g. uint32 + IS_8 => uint8_t
+ */
+
+syntax = "proto2";
+
+import 'nanopb.proto';
+
+message IntSizes {
+    required int32  req_int8 =   1  [(nanopb).int_size = IS_8];
+    required uint32 req_uint8 =  2  [(nanopb).int_size = IS_8];
+    required sint32 req_sint8 =  3  [(nanopb).int_size = IS_8];
+    required int32  req_int16 =  4  [(nanopb).int_size = IS_16];
+    required uint32 req_uint16 = 5  [(nanopb).int_size = IS_16];
+    required sint32 req_sint16 = 6  [(nanopb).int_size = IS_16];
+    required int32  req_int32 =  7  [(nanopb).int_size = IS_32];
+    required uint32 req_uint32 = 8  [(nanopb).int_size = IS_32];
+    required sint32 req_sint32 = 9  [(nanopb).int_size = IS_32];
+    required int32  req_int64 =  10 [(nanopb).int_size = IS_64];
+    required uint32 req_uint64 = 11 [(nanopb).int_size = IS_64];
+    required sint32 req_sint64 = 12 [(nanopb).int_size = IS_64];
+}
+
+message DefaultSizes {
+    required int32  req_int8 =   1 ;
+    required uint32 req_uint8 =  2 ;
+    required sint32 req_sint8 =  3 ;
+    required int32  req_int16 =  4 ;
+    required uint32 req_uint16 = 5 ;
+    required sint32 req_sint16 = 6 ;
+    required int32  req_int32 =  7 ;
+    required uint32 req_uint32 = 8 ;
+    required sint32 req_sint32 = 9 ;
+    required int64  req_int64 =  10;
+    required uint64 req_uint64 = 11;
+    required sint64 req_sint64 = 12;
+}
diff --git a/third_party/nanopb/tests/intsizes/intsizes_unittests.c b/third_party/nanopb/tests/intsizes/intsizes_unittests.c
new file mode 100644
index 0000000..79ef036
--- /dev/null
+++ b/third_party/nanopb/tests/intsizes/intsizes_unittests.c
@@ -0,0 +1,122 @@
+#include <stdio.h>
+#include <string.h>
+#include <pb_decode.h>
+#include <pb_encode.h>
+#include "unittests.h"
+#include "intsizes.pb.h"
+
+#define S(x) pb_istream_from_buffer((uint8_t*)x, sizeof(x) - 1)
+
+/* This is a macro instead of function in order to get the actual values
+ * into the TEST() lines in output */
+#define TEST_ROUNDTRIP(int8, uint8, sint8, \
+                       int16, uint16, sint16, \
+                       int32, uint32, sint32, \
+                       int64, uint64, sint64, expected_result) \
+{                                                                           \
+    uint8_t buffer1[128], buffer2[128];                                     \
+    size_t msgsize;                                                         \
+    DefaultSizes msg1 = DefaultSizes_init_zero;                             \
+    IntSizes msg2 = IntSizes_init_zero;                                     \
+                                                                            \
+    msg1.req_int8   = int8;                                                 \
+    msg1.req_uint8  = uint8;                                                \
+    msg1.req_sint8  = sint8;                                                \
+    msg1.req_int16  = int16;                                                \
+    msg1.req_uint16 = uint16;                                               \
+    msg1.req_sint16 = sint16;                                               \
+    msg1.req_int32  = int32;                                                \
+    msg1.req_uint32 = uint32;                                               \
+    msg1.req_sint32 = sint32;                                               \
+    msg1.req_int64  = int64;                                                \
+    msg1.req_uint64 = uint64;                                               \
+    msg1.req_sint64 = sint64;                                               \
+                                                                            \
+    {                                                                       \
+        pb_ostream_t s = pb_ostream_from_buffer(buffer1, sizeof(buffer1));  \
+        TEST(pb_encode(&s, DefaultSizes_fields, &msg1));                    \
+        msgsize = s.bytes_written;                                          \
+    }                                                                       \
+                                                                            \
+    {                                                                       \
+        pb_istream_t s = pb_istream_from_buffer(buffer1, msgsize);          \
+        TEST(pb_decode(&s, IntSizes_fields, &msg2) == expected_result);     \
+        if (expected_result)                                                \
+        {                                                                   \
+            TEST( (int64_t)msg2.req_int8   == int8);                        \
+            TEST((uint64_t)msg2.req_uint8  == uint8);                       \
+            TEST( (int64_t)msg2.req_sint8  == sint8);                       \
+            TEST( (int64_t)msg2.req_int16  == int16);                       \
+            TEST((uint64_t)msg2.req_uint16 == uint16);                      \
+            TEST( (int64_t)msg2.req_sint16 == sint16);                      \
+            TEST( (int64_t)msg2.req_int32  == int32);                       \
+            TEST((uint64_t)msg2.req_uint32 == uint32);                      \
+            TEST( (int64_t)msg2.req_sint32 == sint32);                      \
+            TEST( (int64_t)msg2.req_int64  == int64);                       \
+            TEST((uint64_t)msg2.req_uint64 == uint64);                      \
+            TEST( (int64_t)msg2.req_sint64 == sint64);                      \
+        }                                                                   \
+    }                                                                       \
+                                                                            \
+    if (expected_result)                                                    \
+    {                                                                       \
+        pb_ostream_t s = pb_ostream_from_buffer(buffer2, sizeof(buffer2));  \
+        TEST(pb_encode(&s, IntSizes_fields, &msg2));                        \
+        TEST(s.bytes_written == msgsize);                                   \
+        TEST(memcmp(buffer1, buffer2, msgsize) == 0);                       \
+    }                                                                       \
+}
+
+int main()
+{
+    int status = 0;
+
+    {
+        IntSizes msg = IntSizes_init_zero;
+
+        COMMENT("Test field sizes");
+        TEST(sizeof(msg.req_int8) == 1);
+        TEST(sizeof(msg.req_uint8) == 1);
+        TEST(sizeof(msg.req_sint8) == 1);
+        TEST(sizeof(msg.req_int16) == 2);
+        TEST(sizeof(msg.req_uint16) == 2);
+        TEST(sizeof(msg.req_sint16) == 2);
+        TEST(sizeof(msg.req_int32) == 4);
+        TEST(sizeof(msg.req_uint32) == 4);
+        TEST(sizeof(msg.req_sint32) == 4);
+        TEST(sizeof(msg.req_int64) == 8);
+        TEST(sizeof(msg.req_uint64) == 8);
+        TEST(sizeof(msg.req_sint64) == 8);
+    }
+
+    COMMENT("Test roundtrip at maximum value");
+    TEST_ROUNDTRIP(127,     255,    127,
+                   32767, 65535,  32767,
+                   INT32_MAX, UINT32_MAX, INT32_MAX,
+                   INT64_MAX, UINT64_MAX, INT64_MAX, true);
+
+    COMMENT("Test roundtrip at minimum value");
+    TEST_ROUNDTRIP(-128,      0,   -128,
+                   -32768,    0, -32768,
+                   INT32_MIN, 0, INT32_MIN,
+                   INT64_MIN, 0, INT64_MIN, true);
+
+    COMMENT("Test overflow detection");
+    TEST_ROUNDTRIP(-129,      0,   -128,
+                   -32768,    0, -32768,
+                   INT32_MIN, 0, INT32_MIN,
+                   INT64_MIN, 0, INT64_MIN, false);
+    TEST_ROUNDTRIP(127,     256,    127,
+                   32767, 65535,  32767,
+                   INT32_MAX, UINT32_MAX, INT32_MAX,
+                   INT64_MAX, UINT64_MAX, INT64_MAX, false);
+    TEST_ROUNDTRIP(-128,      0,   -128,
+                   -32768,    0, -32769,
+                   INT32_MIN, 0, INT32_MIN,
+                   INT64_MIN, 0, INT64_MIN, false);
+
+    if (status != 0)
+        fprintf(stdout, "\n\nSome tests FAILED!\n");
+
+    return status;
+}
\ No newline at end of file
diff --git a/third_party/nanopb/tests/io_errors/SConscript b/third_party/nanopb/tests/io_errors/SConscript
new file mode 100644
index 0000000..60146cc
--- /dev/null
+++ b/third_party/nanopb/tests/io_errors/SConscript
@@ -0,0 +1,15 @@
+# Simulate io errors when encoding and decoding
+
+Import("env")
+
+c = Copy("$TARGET", "$SOURCE")
+env.Command("alltypes.proto", "#alltypes/alltypes.proto", c)
+
+env.NanopbProto(["alltypes", "alltypes.options"])
+
+ioerr = env.Program(["io_errors.c", "alltypes.pb.c",
+                     "$COMMON/pb_encode.o", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"])
+
+env.RunTest("io_errors.output", [ioerr, "$BUILD/alltypes/encode_alltypes.output"])
+
+
diff --git a/third_party/nanopb/tests/io_errors/alltypes.options b/third_party/nanopb/tests/io_errors/alltypes.options
new file mode 100644
index 0000000..b31e3cf
--- /dev/null
+++ b/third_party/nanopb/tests/io_errors/alltypes.options
@@ -0,0 +1,3 @@
+* max_size:16
+* max_count:5
+
diff --git a/third_party/nanopb/tests/io_errors/io_errors.c b/third_party/nanopb/tests/io_errors/io_errors.c
new file mode 100644
index 0000000..76f35b0
--- /dev/null
+++ b/third_party/nanopb/tests/io_errors/io_errors.c
@@ -0,0 +1,140 @@
+/* Simulate IO errors after each byte in a stream.
+ * Verifies proper error propagation.
+ */
+
+#include <stdio.h>
+#include <pb_decode.h>
+#include <pb_encode.h>
+#include "alltypes.pb.h"
+#include "test_helpers.h"
+
+typedef struct
+{
+    uint8_t *buffer;
+    size_t fail_after;
+} faulty_stream_t;
+
+bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
+{
+    faulty_stream_t *state = stream->state;
+    
+    while (count--)
+    {
+        if (state->fail_after == 0)
+            PB_RETURN_ERROR(stream, "simulated");
+        state->fail_after--;
+        *buf++ = *state->buffer++;
+    }
+    
+    return true;
+}
+bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
+{
+    faulty_stream_t *state = stream->state;
+    
+    while (count--)
+    {
+        if (state->fail_after == 0)
+            PB_RETURN_ERROR(stream, "simulated");
+        state->fail_after--;
+        *state->buffer++ = *buf++;
+    }
+    
+    return true;
+}
+
+int main()
+{
+    uint8_t buffer[2048];
+    size_t msglen;
+    AllTypes msg = AllTypes_init_zero;
+    
+    /* Get some base data to run the tests with */
+    SET_BINARY_MODE(stdin);
+    msglen = fread(buffer, 1, sizeof(buffer), stdin);
+    
+    /* Test IO errors on decoding */
+    {
+        bool status;
+        pb_istream_t stream = {&read_callback, NULL, SIZE_MAX};
+        faulty_stream_t fs;
+        size_t i;
+        
+        for (i = 0; i < msglen; i++)
+        {
+            stream.bytes_left = msglen;
+            stream.state = &fs;
+            fs.buffer = buffer;
+            fs.fail_after = i;
+
+            status = pb_decode(&stream, AllTypes_fields, &msg);
+            if (status != false)
+            {
+                fprintf(stderr, "Unexpected success in decode\n");
+                return 2;
+            }
+            else if (strcmp(stream.errmsg, "simulated") != 0)
+            {
+                fprintf(stderr, "Wrong error in decode: %s\n", stream.errmsg);
+                return 3;
+            }
+        }
+        
+        stream.bytes_left = msglen;
+        stream.state = &fs;
+        fs.buffer = buffer;
+        fs.fail_after = msglen;
+        status = pb_decode(&stream, AllTypes_fields, &msg);
+        
+        if (!status)
+        {
+            fprintf(stderr, "Decoding failed: %s\n", stream.errmsg);
+            return 4;
+        }
+    }
+    
+    /* Test IO errors on encoding */
+    {
+        bool status;
+        pb_ostream_t stream = {&write_callback, NULL, SIZE_MAX, 0};
+        faulty_stream_t fs;
+        size_t i;
+        
+        for (i = 0; i < msglen; i++)
+        {
+            stream.max_size = msglen;
+            stream.bytes_written = 0;
+            stream.state = &fs;
+            fs.buffer = buffer;
+            fs.fail_after = i;
+
+            status = pb_encode(&stream, AllTypes_fields, &msg);
+            if (status != false)
+            {
+                fprintf(stderr, "Unexpected success in encode\n");
+                return 5;
+            }
+            else if (strcmp(stream.errmsg, "simulated") != 0)
+            {
+                fprintf(stderr, "Wrong error in encode: %s\n", stream.errmsg);
+                return 6;
+            }
+        }
+        
+        stream.max_size = msglen;
+        stream.bytes_written = 0;
+        stream.state = &fs;
+        fs.buffer = buffer;
+        fs.fail_after = msglen;
+        status = pb_encode(&stream, AllTypes_fields, &msg);
+        
+        if (!status)
+        {
+            fprintf(stderr, "Encoding failed: %s\n", stream.errmsg);
+            return 7;
+        }
+    }
+
+    return 0;   
+}
+
diff --git a/third_party/nanopb/tests/io_errors_pointers/SConscript b/third_party/nanopb/tests/io_errors_pointers/SConscript
new file mode 100644
index 0000000..03727df
--- /dev/null
+++ b/third_party/nanopb/tests/io_errors_pointers/SConscript
@@ -0,0 +1,26 @@
+# Simulate io errors when encoding and decoding
+
+Import("env", "malloc_env")
+
+c = Copy("$TARGET", "$SOURCE")
+env.Command("alltypes.proto", "#alltypes/alltypes.proto", c)
+env.Command("io_errors.c", "#io_errors/io_errors.c", c)
+
+env.NanopbProto(["alltypes", "alltypes.options"])
+
+ioerr = env.Program(["io_errors.c", "alltypes.pb.c",
+                     "$COMMON/pb_encode_with_malloc.o",
+                     "$COMMON/pb_decode_with_malloc.o",
+                     "$COMMON/pb_common_with_malloc.o",
+                     "$COMMON/malloc_wrappers.o"])
+
+# Run tests under valgrind if available
+valgrind = env.WhereIs('valgrind')
+kwargs = {}
+if valgrind:
+    kwargs['COMMAND'] = valgrind
+    kwargs['ARGS'] = ["-q", "--error-exitcode=99", ioerr[0].abspath]
+
+env.RunTest("io_errors.output", [ioerr, "$BUILD/alltypes/encode_alltypes.output"], **kwargs)
+
+
diff --git a/third_party/nanopb/tests/io_errors_pointers/alltypes.options b/third_party/nanopb/tests/io_errors_pointers/alltypes.options
new file mode 100644
index 0000000..52abeb7
--- /dev/null
+++ b/third_party/nanopb/tests/io_errors_pointers/alltypes.options
@@ -0,0 +1,3 @@
+# Generate all fields as pointers.
+* type:FT_POINTER
+
diff --git a/third_party/nanopb/tests/mem_release/SConscript b/third_party/nanopb/tests/mem_release/SConscript
new file mode 100644
index 0000000..6754e28
--- /dev/null
+++ b/third_party/nanopb/tests/mem_release/SConscript
@@ -0,0 +1,13 @@
+Import("env", "malloc_env")
+
+env.NanopbProto("mem_release.proto")
+
+test = malloc_env.Program(["mem_release.c",
+                    "mem_release.pb.c",
+                    "$COMMON/pb_encode_with_malloc.o",
+                    "$COMMON/pb_decode_with_malloc.o",
+                    "$COMMON/pb_common_with_malloc.o",
+                    "$COMMON/malloc_wrappers.o"])
+
+env.RunTest(test)
+
diff --git a/third_party/nanopb/tests/mem_release/mem_release.c b/third_party/nanopb/tests/mem_release/mem_release.c
new file mode 100644
index 0000000..dc6f87d
--- /dev/null
+++ b/third_party/nanopb/tests/mem_release/mem_release.c
@@ -0,0 +1,187 @@
+/* Make sure that all fields are freed in various scenarios. */
+
+#include <pb_decode.h>
+#include <pb_encode.h>
+#include <malloc_wrappers.h>
+#include <stdio.h>
+#include <test_helpers.h>
+#include "mem_release.pb.h"
+
+#define TEST(x) if (!(x)) { \
+    fprintf(stderr, "Test " #x " on line %d failed.\n", __LINE__); \
+    return false; \
+    }
+
+static char *test_str_arr[] = {"1", "2", ""};
+static SubMessage test_msg_arr[] = {SubMessage_init_zero, SubMessage_init_zero};
+static pb_extension_t ext1, ext2;
+
+static void fill_TestMessage(TestMessage *msg)
+{
+    msg->static_req_submsg.dynamic_str = "12345";
+    msg->static_req_submsg.dynamic_str_arr_count = 3;
+    msg->static_req_submsg.dynamic_str_arr = test_str_arr;
+    msg->static_req_submsg.dynamic_submsg_count = 2;
+    msg->static_req_submsg.dynamic_submsg = test_msg_arr;
+    msg->static_req_submsg.dynamic_submsg[1].dynamic_str = "abc";
+    msg->static_opt_submsg.dynamic_str = "abc";
+    msg->static_rep_submsg_count = 2;
+    msg->static_rep_submsg[1].dynamic_str = "abc";
+    msg->has_static_opt_submsg = true;
+    msg->dynamic_submsg = &msg->static_req_submsg;
+
+    msg->extensions = &ext1;
+    ext1.type = &dynamic_ext;
+    ext1.dest = &msg->static_req_submsg;
+    ext1.next = &ext2;
+    ext2.type = &static_ext;
+    ext2.dest = &msg->static_req_submsg;
+    ext2.next = NULL;
+}
+
+/* Basic fields, nested submessages, extensions */
+static bool test_TestMessage()
+{
+    uint8_t buffer[256];
+    size_t msgsize;
+    
+    /* Construct a message with various fields filled in */
+    {
+        TestMessage msg = TestMessage_init_zero;
+        pb_ostream_t stream;
+
+        fill_TestMessage(&msg);
+        
+        stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+        if (!pb_encode(&stream, TestMessage_fields, &msg))
+        {
+            fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&stream));
+            return false;
+        }
+        msgsize = stream.bytes_written;
+    }
+    
+    /* Output encoded message for debug */
+    SET_BINARY_MODE(stdout);
+    fwrite(buffer, 1, msgsize, stdout);
+    
+    /* Decode memory using dynamic allocation */
+    {
+        TestMessage msg = TestMessage_init_zero;
+        pb_istream_t stream;
+        SubMessage ext2_dest;
+
+        msg.extensions = &ext1;
+        ext1.type = &dynamic_ext;
+        ext1.dest = NULL;
+        ext1.next = &ext2;
+        ext2.type = &static_ext;
+        ext2.dest = &ext2_dest;
+        ext2.next = NULL;
+        
+        stream = pb_istream_from_buffer(buffer, msgsize);
+        if (!pb_decode(&stream, TestMessage_fields, &msg))
+        {
+            fprintf(stderr, "Decode failed: %s\n", PB_GET_ERROR(&stream));
+            return false;
+        }
+        
+        /* Make sure it encodes back to same data */
+        {
+            uint8_t buffer2[256];
+            pb_ostream_t ostream = pb_ostream_from_buffer(buffer2, sizeof(buffer2));
+            TEST(pb_encode(&ostream, TestMessage_fields, &msg));
+            TEST(ostream.bytes_written == msgsize);
+            TEST(memcmp(buffer, buffer2, msgsize) == 0);
+        }
+        
+        /* Make sure that malloc counters work */
+        TEST(get_alloc_count() > 0);
+        
+        /* Make sure that pb_release releases everything */
+        pb_release(TestMessage_fields, &msg);
+        TEST(get_alloc_count() == 0);
+        
+        /* Check that double-free is a no-op */
+        pb_release(TestMessage_fields, &msg);
+        TEST(get_alloc_count() == 0);
+    }
+    
+    return true;
+}
+
+/* Oneofs */
+static bool test_OneofMessage()
+{
+    uint8_t buffer[256];
+    size_t msgsize;
+
+    {
+        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+
+        /* Encode first with TestMessage */
+        {
+            OneofMessage msg = OneofMessage_init_zero;
+            msg.which_msgs = OneofMessage_msg1_tag;
+
+            fill_TestMessage(&msg.msgs.msg1);
+
+            if (!pb_encode(&stream, OneofMessage_fields, &msg))
+            {
+                fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&stream));
+                return false;
+            }
+        }
+
+        /* Encode second with SubMessage, invoking 'merge' behaviour */
+        {
+            OneofMessage msg = OneofMessage_init_zero;
+            msg.which_msgs = OneofMessage_msg2_tag;
+
+            msg.first = 999;
+            msg.msgs.msg2.dynamic_str = "ABCD";
+            msg.last = 888;
+
+            if (!pb_encode(&stream, OneofMessage_fields, &msg))
+            {
+                fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&stream));
+                return false;
+            }
+        }
+        msgsize = stream.bytes_written;
+    }
+
+    {
+        OneofMessage msg = OneofMessage_init_zero;
+        pb_istream_t stream = pb_istream_from_buffer(buffer, msgsize);
+        if (!pb_decode(&stream, OneofMessage_fields, &msg))
+        {
+            fprintf(stderr, "Decode failed: %s\n", PB_GET_ERROR(&stream));
+            return false;
+        }
+
+        TEST(msg.first == 999);
+        TEST(msg.which_msgs == OneofMessage_msg2_tag);
+        TEST(msg.msgs.msg2.dynamic_str);
+        TEST(strcmp(msg.msgs.msg2.dynamic_str, "ABCD") == 0);
+        TEST(msg.msgs.msg2.dynamic_str_arr == NULL);
+        TEST(msg.msgs.msg2.dynamic_submsg == NULL);
+        TEST(msg.last == 888);
+
+        pb_release(OneofMessage_fields, &msg);
+        TEST(get_alloc_count() == 0);
+        pb_release(OneofMessage_fields, &msg);
+        TEST(get_alloc_count() == 0);
+    }
+
+    return true;
+}
+
+int main()
+{
+    if (test_TestMessage() && test_OneofMessage())
+        return 0;
+    else
+        return 1;
+}
+
diff --git a/third_party/nanopb/tests/mem_release/mem_release.proto b/third_party/nanopb/tests/mem_release/mem_release.proto
new file mode 100644
index 0000000..0816dc2
--- /dev/null
+++ b/third_party/nanopb/tests/mem_release/mem_release.proto
@@ -0,0 +1,35 @@
+syntax = "proto2";
+import "nanopb.proto";
+
+message SubMessage
+{
+    optional string dynamic_str = 1 [(nanopb).type = FT_POINTER];
+    repeated string dynamic_str_arr = 2 [(nanopb).type = FT_POINTER];
+    repeated SubMessage dynamic_submsg = 3 [(nanopb).type = FT_POINTER];
+}
+
+message TestMessage
+{
+    required SubMessage static_req_submsg = 1 [(nanopb).type = FT_STATIC];
+    optional SubMessage dynamic_submsg = 2 [(nanopb).type = FT_POINTER];
+    optional SubMessage static_opt_submsg = 3 [(nanopb).type = FT_STATIC];
+    repeated SubMessage static_rep_submsg = 4 [(nanopb).type = FT_STATIC, (nanopb).max_count=2];
+    extensions 100 to 200;
+}
+
+extend TestMessage
+{
+    optional SubMessage dynamic_ext = 100 [(nanopb).type = FT_POINTER];
+    optional SubMessage static_ext = 101 [(nanopb).type = FT_STATIC];
+}
+
+message OneofMessage
+{
+    required int32 first = 1;
+    oneof msgs
+    {
+        TestMessage msg1 = 2;
+        SubMessage msg2 = 3;
+    }
+    required int32 last = 4;
+}
diff --git a/third_party/nanopb/tests/message_sizes/SConscript b/third_party/nanopb/tests/message_sizes/SConscript
new file mode 100644
index 0000000..e7524e0
--- /dev/null
+++ b/third_party/nanopb/tests/message_sizes/SConscript
@@ -0,0 +1,11 @@
+# Test the generation of message size #defines
+
+Import('env')
+
+incpath = env.Clone()
+incpath.Append(PROTOCPATH = '#message_sizes')
+
+incpath.NanopbProto("messages1")
+incpath.NanopbProto("messages2")
+
+incpath.Program(['dummy.c', 'messages1.pb.c', 'messages2.pb.c'])
diff --git a/third_party/nanopb/tests/message_sizes/dummy.c b/third_party/nanopb/tests/message_sizes/dummy.c
new file mode 100644
index 0000000..767ad46
--- /dev/null
+++ b/third_party/nanopb/tests/message_sizes/dummy.c
@@ -0,0 +1,9 @@
+/* Just test that the file can be compiled successfully. */
+
+#include "messages2.pb.h"
+
+int main()
+{
+    return xmit_size;
+}
+
diff --git a/third_party/nanopb/tests/message_sizes/messages1.proto b/third_party/nanopb/tests/message_sizes/messages1.proto
new file mode 100644
index 0000000..b66fad7
--- /dev/null
+++ b/third_party/nanopb/tests/message_sizes/messages1.proto
@@ -0,0 +1,29 @@
+syntax = "proto2";
+
+enum MessageStatus {
+    FAIL = 0;
+    OK = 1;
+};
+
+message MessageInfo {
+   required fixed32 msg_id = 1;
+   optional fixed32 interface_id = 2;
+}
+
+message MessageResponseInfo {
+   required fixed64 interface_id = 1;
+   required fixed32 seq = 2;
+   required fixed32 msg_id = 3;
+}
+
+message MessageHeader {
+   required MessageInfo info = 1;
+   optional MessageResponseInfo response_info = 2;
+   optional MessageResponse response = 3;
+}
+
+message MessageResponse {
+   required MessageStatus status = 1;
+   required fixed32 seq = 2;
+}
+
diff --git a/third_party/nanopb/tests/message_sizes/messages2.proto b/third_party/nanopb/tests/message_sizes/messages2.proto
new file mode 100644
index 0000000..6761408
--- /dev/null
+++ b/third_party/nanopb/tests/message_sizes/messages2.proto
@@ -0,0 +1,10 @@
+syntax = "proto2";
+
+import 'nanopb.proto';
+import 'messages1.proto';
+
+message xmit {
+   required MessageHeader header = 1;
+   required bytes data = 2 [(nanopb).max_size = 128];
+}
+
diff --git a/third_party/nanopb/tests/missing_fields/SConscript b/third_party/nanopb/tests/missing_fields/SConscript
new file mode 100644
index 0000000..86ba083
--- /dev/null
+++ b/third_party/nanopb/tests/missing_fields/SConscript
@@ -0,0 +1,8 @@
+# Check that the decoder properly detects when required fields are missing.
+
+Import("env")
+
+env.NanopbProto("missing_fields")
+test = env.Program(["missing_fields.c", "missing_fields.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"])
+env.RunTest(test)
+
diff --git a/third_party/nanopb/tests/missing_fields/missing_fields.c b/third_party/nanopb/tests/missing_fields/missing_fields.c
new file mode 100644
index 0000000..8aded82
--- /dev/null
+++ b/third_party/nanopb/tests/missing_fields/missing_fields.c
@@ -0,0 +1,53 @@
+/* Checks that missing required fields are detected properly */
+
+#include <stdio.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+#include "missing_fields.pb.h"
+
+int main()
+{
+    uint8_t buffer[512];
+    size_t size;
+    
+    /* Create a message with one missing field */
+    {
+        MissingField msg = {0};
+        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+        
+        if (!pb_encode(&stream, MissingField_fields, &msg))
+        {
+            printf("Encode failed.\n");
+            return 1;
+        }
+
+        size = stream.bytes_written;
+    }
+
+    /* Test that it decodes properly if we don't require that field */
+    {
+        MissingField msg = {0};
+        pb_istream_t stream = pb_istream_from_buffer(buffer, size);
+        
+        if (!pb_decode(&stream, MissingField_fields, &msg))
+        {
+            printf("Decode failed: %s\n", PB_GET_ERROR(&stream));
+            return 2;
+        }
+    }
+    
+    /* Test that it does *not* decode properly if we require the field */
+    {
+        AllFields msg = {0};
+        pb_istream_t stream = pb_istream_from_buffer(buffer, size);
+        
+        if (pb_decode(&stream, AllFields_fields, &msg))
+        {
+            printf("Decode didn't detect missing field.\n");
+            return 3;
+        }
+    }
+    
+    return 0; /* All ok */
+}
+
diff --git a/third_party/nanopb/tests/missing_fields/missing_fields.proto b/third_party/nanopb/tests/missing_fields/missing_fields.proto
new file mode 100644
index 0000000..cc5e550
--- /dev/null
+++ b/third_party/nanopb/tests/missing_fields/missing_fields.proto
@@ -0,0 +1,140 @@
+/* Test for one missing field among many */
+
+syntax = "proto2";
+
+message AllFields
+{
+    required int32 field1 = 1;
+    required int32 field2 = 2;
+    required int32 field3 = 3;
+    required int32 field4 = 4;
+    required int32 field5 = 5;
+    required int32 field6 = 6;
+    required int32 field7 = 7;
+    required int32 field8 = 8;
+    required int32 field9 = 9;
+    required int32 field10 = 10;
+    required int32 field11 = 11;
+    required int32 field12 = 12;
+    required int32 field13 = 13;
+    required int32 field14 = 14;
+    required int32 field15 = 15;
+    required int32 field16 = 16;
+    required int32 field17 = 17;
+    required int32 field18 = 18;
+    required int32 field19 = 19;
+    required int32 field20 = 20;
+    required int32 field21 = 21;
+    required int32 field22 = 22;
+    required int32 field23 = 23;
+    required int32 field24 = 24;
+    required int32 field25 = 25;
+    required int32 field26 = 26;
+    required int32 field27 = 27;
+    required int32 field28 = 28;
+    required int32 field29 = 29;
+    required int32 field30 = 30;
+    required int32 field31 = 31;
+    required int32 field32 = 32;
+    required int32 field33 = 33;
+    required int32 field34 = 34;
+    required int32 field35 = 35;
+    required int32 field36 = 36;
+    required int32 field37 = 37;
+    required int32 field38 = 38;
+    required int32 field39 = 39;
+    required int32 field40 = 40;
+    required int32 field41 = 41;
+    required int32 field42 = 42;
+    required int32 field43 = 43;
+    required int32 field44 = 44;
+    required int32 field45 = 45;
+    required int32 field46 = 46;
+    required int32 field47 = 47;
+    required int32 field48 = 48;
+    required int32 field49 = 49;
+    required int32 field50 = 50;
+    required int32 field51 = 51;
+    required int32 field52 = 52;
+    required int32 field53 = 53;
+    required int32 field54 = 54;
+    required int32 field55 = 55;
+    required int32 field56 = 56;
+    required int32 field57 = 57;
+    required int32 field58 = 58;
+    required int32 field59 = 59;
+    required int32 field60 = 60;
+    required int32 field61 = 61;
+    required int32 field62 = 62;
+    required int32 field63 = 63;
+    required int32 field64 = 64;
+}
+
+message MissingField
+{
+    required int32 field1 = 1;
+    required int32 field2 = 2;
+    required int32 field3 = 3;
+    required int32 field4 = 4;
+    required int32 field5 = 5;
+    required int32 field6 = 6;
+    required int32 field7 = 7;
+    required int32 field8 = 8;
+    required int32 field9 = 9;
+    required int32 field10 = 10;
+    required int32 field11 = 11;
+    required int32 field12 = 12;
+    required int32 field13 = 13;
+    required int32 field14 = 14;
+    required int32 field15 = 15;
+    required int32 field16 = 16;
+    required int32 field17 = 17;
+    required int32 field18 = 18;
+    required int32 field19 = 19;
+    required int32 field20 = 20;
+    required int32 field21 = 21;
+    required int32 field22 = 22;
+    required int32 field23 = 23;
+    required int32 field24 = 24;
+    required int32 field25 = 25;
+    required int32 field26 = 26;
+    required int32 field27 = 27;
+    required int32 field28 = 28;
+    required int32 field29 = 29;
+    required int32 field30 = 30;
+    required int32 field31 = 31;
+    required int32 field32 = 32;
+    required int32 field33 = 33;
+    required int32 field34 = 34;
+    required int32 field35 = 35;
+    required int32 field36 = 36;
+    required int32 field37 = 37;
+    required int32 field38 = 38;
+    required int32 field39 = 39;
+    required int32 field40 = 40;
+    required int32 field41 = 41;
+    required int32 field42 = 42;
+    required int32 field43 = 43;
+    required int32 field44 = 44;
+    required int32 field45 = 45;
+    required int32 field46 = 46;
+    required int32 field47 = 47;
+    required int32 field48 = 48;
+    required int32 field49 = 49;
+    required int32 field50 = 50;
+    required int32 field51 = 51;
+    required int32 field52 = 52;
+    required int32 field53 = 53;
+    required int32 field54 = 54;
+    required int32 field55 = 55;
+    required int32 field56 = 56;
+    required int32 field57 = 57;
+    required int32 field58 = 58;
+    required int32 field59 = 59;
+    required int32 field60 = 60;
+    required int32 field61 = 61;
+    required int32 field62 = 62;
+/*    required int32 field63 = 63; */
+    required int32 field64 = 64;
+}
+
diff --git a/third_party/nanopb/tests/multiple_files/SConscript b/third_party/nanopb/tests/multiple_files/SConscript
new file mode 100644
index 0000000..b1281e1
--- /dev/null
+++ b/third_party/nanopb/tests/multiple_files/SConscript
@@ -0,0 +1,16 @@
+# Test that multiple .proto files don't cause name collisions.
+
+Import("env")
+
+incpath = env.Clone()
+incpath.Append(PROTOCPATH = '#multiple_files')
+incpath.Append(CPPPATH = '$BUILD/multiple_files')
+
+incpath.NanopbProto(["multifile1", "multifile1.options"])
+incpath.NanopbProto("multifile2")
+incpath.NanopbProto("subdir/multifile2")
+test = incpath.Program(["test_multiple_files.c", "multifile1.pb.c",
+                        "multifile2.pb.c", "subdir/multifile2.pb.c"])
+
+env.RunTest(test)
+
diff --git a/third_party/nanopb/tests/multiple_files/multifile1.options b/third_party/nanopb/tests/multiple_files/multifile1.options
new file mode 100644
index 0000000..c44d266
--- /dev/null
+++ b/third_party/nanopb/tests/multiple_files/multifile1.options
@@ -0,0 +1 @@
+StaticMessage.repint32 max_count:5
diff --git a/third_party/nanopb/tests/multiple_files/multifile1.proto b/third_party/nanopb/tests/multiple_files/multifile1.proto
new file mode 100644
index 0000000..18f2c67
--- /dev/null
+++ b/third_party/nanopb/tests/multiple_files/multifile1.proto
@@ -0,0 +1,34 @@
+syntax = "proto2";
+
+message SubMessage {
+    optional string stringvalue = 1;
+    repeated int32 int32value = 2;
+    repeated fixed32 fixed32value = 3;
+    repeated fixed64 fixed64value = 4;
+}
+
+message TestMessage {
+    optional string stringvalue = 1;
+    repeated int32 int32value = 2;
+    repeated fixed32 fixed32value = 3;
+    repeated fixed64 fixed64value = 4;
+    optional SubMessage submsg = 5;
+    repeated string repeatedstring = 6;
+}
+
+message StaticMessage {
+    repeated fixed32 repint32 = 1;
+}
+
+enum SignedEnum {
+    SE_MIN = -128;
+    SE_MAX = 127;
+}
+
+enum UnsignedEnum {
+    UE_MIN = 0;
+    UE_MAX = 255;
+}
+
+
+
diff --git a/third_party/nanopb/tests/multiple_files/multifile2.proto b/third_party/nanopb/tests/multiple_files/multifile2.proto
new file mode 100644
index 0000000..4af45fd
--- /dev/null
+++ b/third_party/nanopb/tests/multiple_files/multifile2.proto
@@ -0,0 +1,22 @@
+// Test if including generated header file for this file + implicit include of
+// multifile2.pb.h still compiles. Used with test_compiles.c.
+syntax = "proto2";
+
+import "multifile1.proto";
+
+message Callback2Message {
+    required TestMessage tstmsg = 1;
+    required SubMessage submsg = 2;
+}
+
+message OneofMessage {
+    oneof msgs {
+        StaticMessage tstmsg = 1;
+    }
+}
+
+message Enums {
+    required SignedEnum senum = 1;
+    required UnsignedEnum uenum = 2;
+}
+
diff --git a/third_party/nanopb/tests/multiple_files/subdir/multifile2.proto b/third_party/nanopb/tests/multiple_files/subdir/multifile2.proto
new file mode 100644
index 0000000..847a929
--- /dev/null
+++ b/third_party/nanopb/tests/multiple_files/subdir/multifile2.proto
@@ -0,0 +1,25 @@
+syntax = "proto2";
+
+package subdir;
+
+import "multifile1.proto";
+
+message Callback2Message {
+    required TestMessage tstmsg = 1;
+    required SubMessage submsg = 2;
+}
+
+message OneofMessage {
+    oneof msgs {
+        StaticMessage tstmsg = 1;
+    }
+}
+
+message Enums {
+    required SignedEnum senum = 1;
+    required UnsignedEnum uenum = 2;
+}
+
+message SubdirMessage {
+    required int32 foo = 1 [default = 15];
+}
diff --git a/third_party/nanopb/tests/multiple_files/test_multiple_files.c b/third_party/nanopb/tests/multiple_files/test_multiple_files.c
new file mode 100644
index 0000000..70a3e59
--- /dev/null
+++ b/third_party/nanopb/tests/multiple_files/test_multiple_files.c
@@ -0,0 +1,30 @@
+/*
+ * Tests if this still compiles when multiple .proto files are involved.
+ */
+
+#include <stdio.h>
+#include <pb_encode.h>
+#include "unittests.h"
+#include "multifile2.pb.h"
+#include "subdir/multifile2.pb.h"
+
+int main()
+{
+    int status = 0;
+    
+    /* Test that included file options are properly loaded */
+    TEST(OneofMessage_size == 27);
+    
+    /* Check that enum signedness is detected properly */
+    TEST(PB_LTYPE(Enums_fields[0].type) == PB_LTYPE_VARINT);
+    TEST(PB_LTYPE(Enums_fields[1].type) == PB_LTYPE_UVARINT);
+    
+    /* Test that subdir file is correctly included */
+    {
+        subdir_SubdirMessage foo = subdir_SubdirMessage_init_default;
+        TEST(foo.foo == 15);
+        /* TEST(subdir_OneofMessage_size == 27); */ /* TODO: Issue #172 */
+    }
+    
+    return status;
+}
diff --git a/third_party/nanopb/tests/no_errmsg/SConscript b/third_party/nanopb/tests/no_errmsg/SConscript
new file mode 100644
index 0000000..629bfa6
--- /dev/null
+++ b/third_party/nanopb/tests/no_errmsg/SConscript
@@ -0,0 +1,28 @@
+# Run the alltypes test case, but compile with PB_NO_ERRMSG=1
+
+Import("env")
+
+# Take copy of the files for custom build.
+c = Copy("$TARGET", "$SOURCE")
+env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", c)
+env.Command("alltypes.pb.c", "$BUILD/alltypes/alltypes.pb.c", c)
+env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c)
+env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
+
+# Define the compilation options
+opts = env.Clone()
+opts.Append(CPPDEFINES = {'PB_NO_ERRMSG': 1})
+
+# Build new version of core
+strict = opts.Clone()
+strict.Append(CFLAGS = strict['CORECFLAGS'])
+strict.Object("pb_decode_noerr.o", "$NANOPB/pb_decode.c")
+strict.Object("pb_encode_noerr.o", "$NANOPB/pb_encode.c")
+strict.Object("pb_common_noerr.o", "$NANOPB/pb_common.c")
+
+# Now build and run the test normally.
+enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_noerr.o", "pb_common_noerr.o"])
+dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_noerr.o", "pb_common_noerr.o"])
+
+env.RunTest(enc)
+env.RunTest([dec, "encode_alltypes.output"])
diff --git a/third_party/nanopb/tests/no_messages/SConscript b/third_party/nanopb/tests/no_messages/SConscript
new file mode 100644
index 0000000..6492e2c
--- /dev/null
+++ b/third_party/nanopb/tests/no_messages/SConscript
@@ -0,0 +1,7 @@
+# Test that a .proto file without any messages compiles fine.
+
+Import("env")
+
+env.NanopbProto("no_messages")
+env.Object('no_messages.pb.c')
+
diff --git a/third_party/nanopb/tests/no_messages/no_messages.proto b/third_party/nanopb/tests/no_messages/no_messages.proto
new file mode 100644
index 0000000..45bb2e6
--- /dev/null
+++ b/third_party/nanopb/tests/no_messages/no_messages.proto
@@ -0,0 +1,9 @@
+/* Test that a file without any messages works. */
+
+syntax = "proto2";
+
+enum Test {
+    First = 1;
+}
+
+
diff --git a/third_party/nanopb/tests/oneof/SConscript b/third_party/nanopb/tests/oneof/SConscript
new file mode 100644
index 0000000..22634fb
--- /dev/null
+++ b/third_party/nanopb/tests/oneof/SConscript
@@ -0,0 +1,33 @@
+# Test the 'oneof' feature for generating C unions.
+
+Import('env')
+
+import re
+
+match = None
+if 'PROTOC_VERSION' in env:
+    match = re.search('([0-9]+).([0-9]+).([0-9]+)', env['PROTOC_VERSION'])
+
+if match:
+    version = map(int, match.groups())
+
+# Oneof is supported by protoc >= 2.6.0
+if env.GetOption('clean') or (match and (version[0] > 2 or (version[0] == 2 and version[1] >= 6))):
+    env.NanopbProto('oneof')
+
+    enc = env.Program(['encode_oneof.c',
+                    'oneof.pb.c',
+                    '$COMMON/pb_encode.o',
+                    '$COMMON/pb_common.o'])
+
+    dec = env.Program(['decode_oneof.c',
+                    'oneof.pb.c',
+                    '$COMMON/pb_decode.o',
+                    '$COMMON/pb_common.o'])
+
+    env.RunTest("message1.pb", enc, ARGS = ['1'])
+    env.RunTest("message1.txt", [dec, 'message1.pb'], ARGS = ['1'])
+    env.RunTest("message2.pb", enc, ARGS = ['2'])
+    env.RunTest("message2.txt", [dec, 'message2.pb'], ARGS = ['2'])
+    env.RunTest("message3.pb", enc, ARGS = ['3'])
+    env.RunTest("message3.txt", [dec, 'message3.pb'], ARGS = ['3'])
diff --git a/third_party/nanopb/tests/oneof/decode_oneof.c b/third_party/nanopb/tests/oneof/decode_oneof.c
new file mode 100644
index 0000000..37075cd
--- /dev/null
+++ b/third_party/nanopb/tests/oneof/decode_oneof.c
@@ -0,0 +1,131 @@
+/* Decode a message using oneof fields */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_decode.h>
+#include "oneof.pb.h"
+#include "test_helpers.h"
+#include "unittests.h"
+
+/* Test the 'OneOfMessage' */
+int test_oneof_1(pb_istream_t *stream, int option)
+{
+    OneOfMessage msg;
+    int status = 0;
+
+    /* To better catch initialization errors */
+    memset(&msg, 0xAA, sizeof(msg));
+
+    if (!pb_decode(stream, OneOfMessage_fields, &msg))
+    {
+        printf("Decoding failed: %s\n", PB_GET_ERROR(stream));
+        return 1;
+    }
+
+    /* Check that the basic fields work normally */
+    TEST(msg.prefix == 123);
+    TEST(msg.suffix == 321);
+
+    /* Check that we got the right oneof according to command line */
+    if (option == 1)
+    {
+        TEST(msg.which_values == OneOfMessage_first_tag);
+        TEST(msg.values.first == 999);
+    }
+    else if (option == 2)
+    {
+        TEST(msg.which_values == OneOfMessage_second_tag);
+        TEST(strcmp(msg.values.second, "abcd") == 0);
+    }
+    else if (option == 3)
+    {
+        TEST(msg.which_values == OneOfMessage_third_tag);
+        TEST(msg.values.third.array[0] == 1);
+        TEST(msg.values.third.array[1] == 2);
+        TEST(msg.values.third.array[2] == 3);
+        TEST(msg.values.third.array[3] == 4);
+        TEST(msg.values.third.array[4] == 5);
+    }
+
+    return status;
+}
+
+
+/* Test the 'PlainOneOfMessage' */
+int test_oneof_2(pb_istream_t *stream, int option)
+{
+    PlainOneOfMessage msg = PlainOneOfMessage_init_zero;
+    int status = 0;
+
+    if (!pb_decode(stream, PlainOneOfMessage_fields, &msg))
+    {
+        printf("Decoding failed: %s\n", PB_GET_ERROR(stream));
+        return 1;
+    }
+
+    /* Check that we got the right oneof according to command line */
+    if (option == 1)
+    {
+        TEST(msg.which_values == OneOfMessage_first_tag);
+        TEST(msg.values.first == 999);
+    }
+    else if (option == 2)
+    {
+        TEST(msg.which_values == OneOfMessage_second_tag);
+        TEST(strcmp(msg.values.second, "abcd") == 0);
+    }
+    else if (option == 3)
+    {
+        TEST(msg.which_values == OneOfMessage_third_tag);
+        TEST(msg.values.third.array[0] == 1);
+        TEST(msg.values.third.array[1] == 2);
+        TEST(msg.values.third.array[2] == 3);
+        TEST(msg.values.third.array[3] == 4);
+        TEST(msg.values.third.array[4] == 5);
+    }
+
+    return status;
+}
+
+int main(int argc, char **argv)
+{
+    uint8_t buffer[OneOfMessage_size];
+    size_t count;
+    int option;
+
+    if (argc != 2)
+    {
+        fprintf(stderr, "Usage: decode_oneof [number]\n");
+        return 1;
+    }
+    option = atoi(argv[1]);
+
+    SET_BINARY_MODE(stdin);
+    count = fread(buffer, 1, sizeof(buffer), stdin);
+
+    if (!feof(stdin))
+    {
+        printf("Message does not fit in buffer\n");
+        return 1;
+    }
+
+    {
+        int status = 0;
+        pb_istream_t stream;
+
+        stream = pb_istream_from_buffer(buffer, count);
+        status = test_oneof_1(&stream, option);
+
+        if (status != 0)
+            return status;
+
+        stream = pb_istream_from_buffer(buffer, count);
+        status = test_oneof_2(&stream, option);
+
+        if (status != 0)
+            return status;
+    }
+
+    return 0;
+}
diff --git a/third_party/nanopb/tests/oneof/encode_oneof.c b/third_party/nanopb/tests/oneof/encode_oneof.c
new file mode 100644
index 0000000..913d2d4
--- /dev/null
+++ b/third_party/nanopb/tests/oneof/encode_oneof.c
@@ -0,0 +1,64 @@
+/* Encode a message using oneof fields */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pb_encode.h>
+#include "oneof.pb.h"
+#include "test_helpers.h"
+
+int main(int argc, char **argv)
+{
+    uint8_t buffer[OneOfMessage_size];
+    OneOfMessage msg = OneOfMessage_init_zero;
+    pb_ostream_t stream;
+    int option;
+
+    if (argc != 2)
+    {
+        fprintf(stderr, "Usage: encode_oneof [number]\n");
+        return 1;
+    }
+    option = atoi(argv[1]);
+
+    /* Prefix and suffix are used to test that the union does not disturb
+     * other fields in the same message. */
+    msg.prefix = 123;
+
+    /* We encode one of the 'values' fields based on command line argument */
+    if (option == 1)
+    {
+        msg.which_values = OneOfMessage_first_tag;
+        msg.values.first = 999;
+    }
+    else if (option == 2)
+    {
+        msg.which_values = OneOfMessage_second_tag;
+        strcpy(msg.values.second, "abcd");
+    }
+    else if (option == 3)
+    {
+        msg.which_values = OneOfMessage_third_tag;
+        msg.values.third.array_count = 5;
+        msg.values.third.array[0] = 1;
+        msg.values.third.array[1] = 2;
+        msg.values.third.array[2] = 3;
+        msg.values.third.array[3] = 4;
+        msg.values.third.array[4] = 5;
+    }
+
+    msg.suffix = 321;
+
+    stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+
+    if (pb_encode(&stream, OneOfMessage_fields, &msg))
+    {
+        SET_BINARY_MODE(stdout);
+        fwrite(buffer, 1, stream.bytes_written, stdout);
+        return 0;
+    }
+    else
+    {
+        fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
+        return 1;
+    }
+}
diff --git a/third_party/nanopb/tests/oneof/oneof.proto b/third_party/nanopb/tests/oneof/oneof.proto
new file mode 100644
index 0000000..b4fe56f
--- /dev/null
+++ b/third_party/nanopb/tests/oneof/oneof.proto
@@ -0,0 +1,32 @@
+syntax = "proto2";
+
+import 'nanopb.proto';
+
+message SubMessage
+{
+    repeated int32 array = 1 [(nanopb).max_count = 8];
+}
+
+/* Oneof in a message with other fields */
+message OneOfMessage
+{
+    required int32 prefix = 1;
+    oneof values
+    {
+        int32 first = 5;
+        string second = 6 [(nanopb).max_size = 8];
+        SubMessage third = 7;
+    }
+    required int32 suffix = 99;
+}
+
+/* Oneof in a message by itself */
+message PlainOneOfMessage
+{
+    oneof values
+    {
+        int32 first = 5;
+        string second = 6 [(nanopb).max_size = 8];
+        SubMessage third = 7;
+    }
+}
\ No newline at end of file
diff --git a/third_party/nanopb/tests/options/SConscript b/third_party/nanopb/tests/options/SConscript
new file mode 100644
index 0000000..89a00fa
--- /dev/null
+++ b/third_party/nanopb/tests/options/SConscript
@@ -0,0 +1,9 @@
+# Test that the generator options work as expected.
+
+Import("env")
+
+env.NanopbProto("options")
+env.Object('options.pb.c')
+
+env.Match(['options.pb.h', 'options.expected'])
+
diff --git a/third_party/nanopb/tests/options/options.expected b/third_party/nanopb/tests/options/options.expected
new file mode 100644
index 0000000..63ba0fd
--- /dev/null
+++ b/third_party/nanopb/tests/options/options.expected
@@ -0,0 +1,18 @@
+char filesize\[20\];
+char msgsize\[30\];
+char fieldsize\[40\];
+pb_callback_t int32_callback;
+\sEnumValue1 = 1
+Message5_EnumValue1
+} pb_packed my_packed_struct;
+! skipped_field
+! SkippedMessage
+#define PB_MSG_103 Message3
+#define PB_MSG_104 Message4
+#define PB_MSG_105 Message5
+#define OPTIONS_MESSAGES \\
+\s+PB_MSG\(103,[0-9]*,Message3\) \\
+\s+PB_MSG\(104,-1,Message4\) \\
+\s+PB_MSG\(105,[0-9]*,Message5\) \\
+#define Message5_msgid 105
+
diff --git a/third_party/nanopb/tests/options/options.proto b/third_party/nanopb/tests/options/options.proto
new file mode 100644
index 0000000..aa722b5
--- /dev/null
+++ b/third_party/nanopb/tests/options/options.proto
@@ -0,0 +1,91 @@
+/* Test nanopb option parsing.
+ * options.expected lists the patterns that are searched for in the output.
+ */
+
+syntax = "proto2";
+
+import "nanopb.proto";
+
+// File level options
+option (nanopb_fileopt).max_size = 20;
+
+message Message1
+{
+    required string filesize = 1;
+}
+
+// Message level options
+message Message2
+{
+    option (nanopb_msgopt).max_size = 30;
+    required string msgsize = 1;
+}
+
+// Field level options
+message Message3
+{
+    option (nanopb_msgopt).msgid = 103;
+    required string fieldsize = 1 [(nanopb).max_size = 40];
+}
+
+// Forced callback field
+message Message4
+{
+    option (nanopb_msgopt).msgid = 104;
+    required int32 int32_callback = 1 [(nanopb).type = FT_CALLBACK];
+}
+
+// Short enum names
+enum Enum1
+{
+    option (nanopb_enumopt).long_names = false;
+    EnumValue1 = 1;
+    EnumValue2 = 2;
+}
+
+message EnumTest
+{
+    required Enum1 field = 1 [default = EnumValue2];
+}
+
+// Short enum names inside message
+message Message5
+{
+    option (nanopb_msgopt).msgid = 105;
+    enum Enum2
+    {
+       option (nanopb_enumopt).long_names = false;
+       EnumValue1 = 1;
+    }
+    required Enum2 field = 1 [default = EnumValue1];
+}
+
+// Packed structure
+message my_packed_struct
+{
+    option (nanopb_msgopt).packed_struct = true;
+    optional int32 myfield = 1;
+}
+
+// Message with ignored field
+message Message6
+{
+    required int32 field1 = 1;
+    optional int32 skipped_field = 2 [(nanopb).type = FT_IGNORE];
+}
+
+// Message that is skipped
+message SkippedMessage
+{
+    option (nanopb_msgopt).skip_message = true;
+    required int32 foo = 1;
+}
+
+// Message with oneof field
+message OneofMessage
+{
+    oneof foo {
+        int32 bar = 1;
+    }
+}
+
diff --git a/third_party/nanopb/tests/package_name/SConscript b/third_party/nanopb/tests/package_name/SConscript
new file mode 100644
index 0000000..4afc503
--- /dev/null
+++ b/third_party/nanopb/tests/package_name/SConscript
@@ -0,0 +1,38 @@
+# Check that alltypes test case works also when the .proto file defines
+# a package name.
+
+Import("env")
+
+def set_pkgname(src, dst, pkgname):
+    data = open(str(src)).read()
+    placeholder = '// package name placeholder'
+    assert placeholder in data
+    data = data.replace(placeholder, 'package %s;' % pkgname)
+    open(str(dst), 'w').write(data)
+
+# Build a modified alltypes.proto
+env.Command("alltypes.proto", "#alltypes/alltypes.proto",
+            lambda target, source, env: set_pkgname(source[0], target[0], 'test.package'))
+env.Command("alltypes.options", "#alltypes/alltypes.options", Copy("$TARGET", "$SOURCE"))
+env.NanopbProto(["alltypes", "alltypes.options"])
+
+# Build a modified encode_alltypes.c
+def modify_c(target, source, env):
+    '''Add package name to type names in .c file.'''
+
+    data = open(str(source[0]), 'r').read()
+    
+    type_names = ['AllTypes', 'MyEnum', 'HugeEnum']
+    for name in type_names:
+        data = data.replace(name, 'test_package_' + name)
+    
+    open(str(target[0]), 'w').write(data)
+    return 0
+env.Command("encode_alltypes.c", "#alltypes/encode_alltypes.c", modify_c)
+
+# Encode and compare results to original alltypes testcase
+enc = env.Program(["encode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"])
+refdec = "$BUILD/alltypes/decode_alltypes$PROGSUFFIX"
+env.RunTest(enc)
+env.Compare(["encode_alltypes.output", "$BUILD/alltypes/encode_alltypes.output"])
+
diff --git a/third_party/nanopb/tests/regression/issue_118/SConscript b/third_party/nanopb/tests/regression/issue_118/SConscript
new file mode 100644
index 0000000..833d9de
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_118/SConscript
@@ -0,0 +1,12 @@
+# Regression test for Issue 118: Short enum names in imported proto files are not honoured
+
+Import("env")
+env = env.Clone()
+env.Append(PROTOCPATH = "#regression/issue_118")
+
+env.NanopbProto("enumdef")
+env.Object('enumdef.pb.c')
+
+env.NanopbProto(["enumuse", "enumdef.proto"])
+env.Object('enumuse.pb.c')
+
diff --git a/third_party/nanopb/tests/regression/issue_118/enumdef.proto b/third_party/nanopb/tests/regression/issue_118/enumdef.proto
new file mode 100644
index 0000000..46845bc
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_118/enumdef.proto
@@ -0,0 +1,8 @@
+syntax = "proto2";
+
+import 'nanopb.proto';
+
+enum MyEnum {
+	option (nanopb_enumopt).long_names = false;
+	FOOBAR = 1;
+}
diff --git a/third_party/nanopb/tests/regression/issue_118/enumuse.proto b/third_party/nanopb/tests/regression/issue_118/enumuse.proto
new file mode 100644
index 0000000..4afc452
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_118/enumuse.proto
@@ -0,0 +1,7 @@
+syntax = "proto2";
+
+import 'enumdef.proto';
+
+message MyMessage {
+	required MyEnum myenum = 1 [default = FOOBAR];
+}
diff --git a/third_party/nanopb/tests/regression/issue_125/SConscript b/third_party/nanopb/tests/regression/issue_125/SConscript
new file mode 100644
index 0000000..f2155e6
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_125/SConscript
@@ -0,0 +1,9 @@
+# Regression test for Issue 125: Wrong identifier name for extension fields
+
+Import("env")
+
+env.NanopbProto(["extensionbug", "extensionbug.options"])
+env.Object('extensionbug.pb.c')
+
+env.Match(['extensionbug.pb.h', 'extensionbug.expected'])
+
diff --git a/third_party/nanopb/tests/regression/issue_125/extensionbug.expected b/third_party/nanopb/tests/regression/issue_125/extensionbug.expected
new file mode 100644
index 0000000..fc21335
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_125/extensionbug.expected
@@ -0,0 +1,3 @@
+pb_extension_type_t Message2_extras
+uint32_t field2
+
diff --git a/third_party/nanopb/tests/regression/issue_125/extensionbug.options b/third_party/nanopb/tests/regression/issue_125/extensionbug.options
new file mode 100644
index 0000000..30b464a
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_125/extensionbug.options
@@ -0,0 +1,4 @@
+* type:FT_IGNORE
+
+Message2.extras type:FT_STATIC
+Message2.field2 type:FT_STATIC
diff --git a/third_party/nanopb/tests/regression/issue_125/extensionbug.proto b/third_party/nanopb/tests/regression/issue_125/extensionbug.proto
new file mode 100644
index 0000000..fd1e74f
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_125/extensionbug.proto
@@ -0,0 +1,18 @@
+syntax = "proto2";
+
+message Message1
+{
+	optional uint32	fieldA = 1;
+	extensions 30 to max;
+}
+
+message Message2
+{
+	extend Message1
+	{
+		optional Message2 extras = 30;
+	}
+	
+	optional uint32	field1 = 1;
+	optional uint32 field2 = 2;
+}
diff --git a/third_party/nanopb/tests/regression/issue_141/SConscript b/third_party/nanopb/tests/regression/issue_141/SConscript
new file mode 100644
index 0000000..b6526be
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_141/SConscript
@@ -0,0 +1,8 @@
+# Regression test for issue 141: wrong encoded size #define for oneof messages
+
+Import("env")
+
+env.NanopbProto("testproto")
+env.Object('testproto.pb.c')
+env.Match(['testproto.pb.h', 'testproto.expected'])
+
diff --git a/third_party/nanopb/tests/regression/issue_141/testproto.expected b/third_party/nanopb/tests/regression/issue_141/testproto.expected
new file mode 100644
index 0000000..75bc195
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_141/testproto.expected
@@ -0,0 +1,7 @@
+define SubMessage_size \s* 88
+define OneOfMessage_size \s* 113
+define topMessage_size \s* 70
+define MyMessage1_size \s* 46
+define MyMessage2_size \s* 8
+define MyMessage3_size \s* 5
+define MyMessage4_size \s* 18
diff --git a/third_party/nanopb/tests/regression/issue_141/testproto.proto b/third_party/nanopb/tests/regression/issue_141/testproto.proto
new file mode 100644
index 0000000..a445c68
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_141/testproto.proto
@@ -0,0 +1,52 @@
+syntax = "proto2";
+
+import 'nanopb.proto';
+
+message SubMessage
+{
+    repeated int32 array = 1 [(nanopb).max_count = 8];
+}
+
+message OneOfMessage
+{
+    required int32 prefix = 1;
+    oneof values
+    {
+        int32 first = 5;
+        string second = 6 [(nanopb).max_size = 8];
+        SubMessage third = 7;
+    }
+    required int32 suffix = 99;
+}
+
+message topMessage {
+	required int32 start = 1;
+	oneof msg {
+		MyMessage1 msg1 = 2;
+		MyMessage2 msg2 = 3;
+	}
+	required int32 end = 4;
+}
+
+message MyMessage1 {
+	required uint32 n1 = 1;
+	required uint32 n2 = 2;
+	required string s = 3 [(nanopb).max_size = 32];
+}
+
+message MyMessage2 {
+	required uint32 num = 1;
+	required bool b = 2;
+}
+
+message MyMessage3 {
+	required bool bbb = 1;
+	required string ss = 2 [(nanopb).max_size = 1];
+}
+
+message MyMessage4 {
+	required bool bbbb = 1;
+	required string sss = 2 [(nanopb).max_size = 2];
+	required uint32 num = 3;
+	required uint32 num2 = 4;
+}
diff --git a/third_party/nanopb/tests/regression/issue_145/SConscript b/third_party/nanopb/tests/regression/issue_145/SConscript
new file mode 100644
index 0000000..0b793a7
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_145/SConscript
@@ -0,0 +1,9 @@
+# Regression test for Issue 145: Allow /* */ and // comments in .options files
+
+Import("env")
+
+env.NanopbProto(["comments", "comments.options"])
+env.Object('comments.pb.c')
+
+env.Match(['comments.pb.h', 'comments.expected'])
+
diff --git a/third_party/nanopb/tests/regression/issue_145/comments.expected b/third_party/nanopb/tests/regression/issue_145/comments.expected
new file mode 100644
index 0000000..7f87458
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_145/comments.expected
@@ -0,0 +1,3 @@
+char foo\[5\];
+char bar\[16\];
+
diff --git a/third_party/nanopb/tests/regression/issue_145/comments.options b/third_party/nanopb/tests/regression/issue_145/comments.options
new file mode 100644
index 0000000..89959ba
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_145/comments.options
@@ -0,0 +1,6 @@
+/* Block comment */
+# Line comment
+// Line comment
+DummyMessage.foo /* Block comment */ max_size:5
+DummyMessage.bar max_size:16 # Line comment ###
+
diff --git a/third_party/nanopb/tests/regression/issue_145/comments.proto b/third_party/nanopb/tests/regression/issue_145/comments.proto
new file mode 100644
index 0000000..621779f
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_145/comments.proto
@@ -0,0 +1,7 @@
+syntax = "proto2";
+
+message DummyMessage {
+    required string foo = 1;
+    required string bar = 2;
+}
+
diff --git a/third_party/nanopb/tests/regression/issue_166/SConscript b/third_party/nanopb/tests/regression/issue_166/SConscript
new file mode 100644
index 0000000..c50b919
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_166/SConscript
@@ -0,0 +1,13 @@
+# Verify that the maximum encoded size is calculated properly
+# for enums.
+
+Import('env')
+
+env.NanopbProto('enums')
+
+p = env.Program(["enum_encoded_size.c",
+                 "enums.pb.c",
+                 "$COMMON/pb_encode.o",
+                 "$COMMON/pb_common.o"])
+env.RunTest(p)
+
diff --git a/third_party/nanopb/tests/regression/issue_166/enum_encoded_size.c b/third_party/nanopb/tests/regression/issue_166/enum_encoded_size.c
new file mode 100644
index 0000000..84e1c7d
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_166/enum_encoded_size.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <string.h>
+#include <pb_encode.h>
+#include "unittests.h"
+#include "enums.pb.h"
+
+int main()
+{
+    int status = 0;
+    
+    uint8_t buf[256];
+    SignedMsg msg1;
+    UnsignedMsg msg2;
+    pb_ostream_t s;
+    
+    {
+        COMMENT("Test negative value of signed enum");
+        /* Negative value should take up the maximum size */
+        msg1.value = SignedEnum_SE_MIN;
+        s = pb_ostream_from_buffer(buf, sizeof(buf));
+        TEST(pb_encode(&s, SignedMsg_fields, &msg1));
+        TEST(s.bytes_written == SignedMsg_size);
+        
+        COMMENT("Test positive value of signed enum");
+        /* Positive value should be smaller */
+        msg1.value = SignedEnum_SE_MAX;
+        s = pb_ostream_from_buffer(buf, sizeof(buf));
+        TEST(pb_encode(&s, SignedMsg_fields, &msg1));
+        TEST(s.bytes_written < SignedMsg_size);
+    }
+    
+    {
+        COMMENT("Test positive value of unsigned enum");
+        /* This should take up the maximum size */
+        msg2.value = UnsignedEnum_UE_MAX;
+        s = pb_ostream_from_buffer(buf, sizeof(buf));
+        TEST(pb_encode(&s, UnsignedMsg_fields, &msg2));
+        TEST(s.bytes_written == UnsignedMsg_size);
+    }
+    
+    return status;
+}
+
diff --git a/third_party/nanopb/tests/regression/issue_166/enums.proto b/third_party/nanopb/tests/regression/issue_166/enums.proto
new file mode 100644
index 0000000..3694804
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_166/enums.proto
@@ -0,0 +1,18 @@
+syntax = "proto2";
+
+enum SignedEnum {
+    SE_MIN = -1;
+    SE_MAX = 255;
+}
+
+enum UnsignedEnum {
+    UE_MAX = 65536;
+}
+
+message SignedMsg {
+    required SignedEnum value = 1;
+}
+
+message UnsignedMsg {
+    required UnsignedEnum value = 1;
+}
diff --git a/third_party/nanopb/tests/regression/issue_172/SConscript b/third_party/nanopb/tests/regression/issue_172/SConscript
new file mode 100644
index 0000000..49c919e
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_172/SConscript
@@ -0,0 +1,16 @@
+# Verify that _size define is generated for messages that have
+# includes from another directory.
+
+Import('env')
+
+incpath = env.Clone()
+incpath.Append(PROTOCPATH="#regression/issue_172/submessage")
+incpath.Append(CPPPATH="$BUILD/regression/issue_172/submessage")
+incpath.NanopbProto('test')
+incpath.NanopbProto(['submessage/submessage', 'submessage/submessage.options'])
+
+p = incpath.Program(["msg_size.c",
+                     "test.pb.c",
+                     "submessage/submessage.pb.c"])
+
+
diff --git a/third_party/nanopb/tests/regression/issue_172/msg_size.c b/third_party/nanopb/tests/regression/issue_172/msg_size.c
new file mode 100644
index 0000000..be45acb
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_172/msg_size.c
@@ -0,0 +1,9 @@
+#include "test.pb.h"
+
+PB_STATIC_ASSERT(testmessage_size >= 1+1+1+1+16, TESTMESSAGE_SIZE_IS_WRONG)
+
+int main()
+{
+    return 0;
+}
+
diff --git a/third_party/nanopb/tests/regression/issue_172/submessage/submessage.options b/third_party/nanopb/tests/regression/issue_172/submessage/submessage.options
new file mode 100644
index 0000000..12fb198
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_172/submessage/submessage.options
@@ -0,0 +1 @@
+submessage.data max_size: 16
diff --git a/third_party/nanopb/tests/regression/issue_172/submessage/submessage.proto b/third_party/nanopb/tests/regression/issue_172/submessage/submessage.proto
new file mode 100644
index 0000000..ce6804a
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_172/submessage/submessage.proto
@@ -0,0 +1,4 @@
+syntax = "proto2";
+message submessage {
+    required bytes data = 1;
+}
diff --git a/third_party/nanopb/tests/regression/issue_172/test.proto b/third_party/nanopb/tests/regression/issue_172/test.proto
new file mode 100644
index 0000000..fbd97be
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_172/test.proto
@@ -0,0 +1,6 @@
+syntax = "proto2";
+import "submessage.proto";
+
+message testmessage {
+ optional submessage sub = 1;
+}
diff --git a/third_party/nanopb/tests/regression/issue_188/SConscript b/third_party/nanopb/tests/regression/issue_188/SConscript
new file mode 100644
index 0000000..6bc3271
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_188/SConscript
@@ -0,0 +1,6 @@
+# Regression test for issue with Enums inside OneOf.
+
+Import('env')
+
+env.NanopbProto('oneof')
+
diff --git a/third_party/nanopb/tests/regression/issue_188/oneof.proto b/third_party/nanopb/tests/regression/issue_188/oneof.proto
new file mode 100644
index 0000000..e37f5c0
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_188/oneof.proto
@@ -0,0 +1,29 @@
+syntax = "proto2";
+
+message MessageOne
+{
+    required uint32 one = 1;
+    required uint32 two = 2;
+    required uint32 three = 3;
+    required int32 four = 4;
+}
+
+enum EnumTwo
+{
+    SOME_ENUM_1 = 1;
+    SOME_ENUM_2 = 5;
+    SOME_ENUM_3 = 6;
+    SOME_ENUM_4 = 9;
+    SOME_ENUM_5 = 10;
+    SOME_ENUM_6 = 12;
+    SOME_ENUM_7 = 39;  
+    SOME_ENUM_8 = 401;
+}
+
+message OneofMessage
+{
+    oneof payload {
+        MessageOne message = 1;
+        EnumTwo enum = 2;
+    }
+}
diff --git a/third_party/nanopb/tests/regression/issue_195/SConscript b/third_party/nanopb/tests/regression/issue_195/SConscript
new file mode 100644
index 0000000..78326d3
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_195/SConscript
@@ -0,0 +1,10 @@
+# Regression test for Issue 195: Message size not calculated if a submessage includes 
+# bytes. Basically a non-working #define being generated.
+
+Import("env")
+
+env.NanopbProto(["test"])
+env.Object('test.pb.c')
+
+env.Match(['test.pb.h', 'test.expected'])
+
diff --git a/third_party/nanopb/tests/regression/issue_195/test.expected b/third_party/nanopb/tests/regression/issue_195/test.expected
new file mode 100644
index 0000000..83ea7ab
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_195/test.expected
@@ -0,0 +1 @@
+/\* TestMessage_size depends
diff --git a/third_party/nanopb/tests/regression/issue_195/test.proto b/third_party/nanopb/tests/regression/issue_195/test.proto
new file mode 100644
index 0000000..7a77d69
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_195/test.proto
@@ -0,0 +1,8 @@
+message TestMessage {
+    required uint32 id = 1;
+    required bytes payload = 2;
+}
+message EncapsulatedMessage {
+    required uint32 id = 1;
+    required TestMessage test = 2;
+}
diff --git a/third_party/nanopb/tests/regression/issue_203/SConscript b/third_party/nanopb/tests/regression/issue_203/SConscript
new file mode 100644
index 0000000..8b4d6cc
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_203/SConscript
@@ -0,0 +1,9 @@
+# Regression test for issue with multiple files generated at once
+
+Import('env')
+
+env.Command(['file1.pb.c', 'file1.pb.h', 'file2.pb.c', 'file2.pb.h'], ['file1.proto', 'file2.proto'],
+            env['NANOPB_PROTO_CMD'])
+
+env.Object('file1.pb.c')
+env.Object('file2.pb.c')
diff --git a/third_party/nanopb/tests/regression/issue_203/file1.proto b/third_party/nanopb/tests/regression/issue_203/file1.proto
new file mode 100644
index 0000000..dae250b
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_203/file1.proto
@@ -0,0 +1,10 @@
+syntax = "proto2";
+
+message SubMessage1 {
+	required int32 foo = 1;
+}
+
+message Message1 {
+	required SubMessage1 bar = 1;
+}
+
diff --git a/third_party/nanopb/tests/regression/issue_203/file2.proto b/third_party/nanopb/tests/regression/issue_203/file2.proto
new file mode 100644
index 0000000..513b0f0
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_203/file2.proto
@@ -0,0 +1,10 @@
+syntax = "proto2";
+
+message SubMessage2 {
+	required int32 foo = 1;
+}
+
+message Message2 {
+	required SubMessage2 bar = 1;
+}
+
diff --git a/third_party/nanopb/tests/regression/issue_205/SConscript b/third_party/nanopb/tests/regression/issue_205/SConscript
new file mode 100644
index 0000000..ed8899d
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_205/SConscript
@@ -0,0 +1,14 @@
+# Check that pb_release() correctly handles corrupted size fields of
+# static arrays.
+
+Import('env', 'malloc_env')
+
+env.NanopbProto('size_corruption')
+
+p = malloc_env.Program(["size_corruption.c",
+                         "size_corruption.pb.c",
+                         "$COMMON/pb_decode_with_malloc.o",
+                         "$COMMON/pb_common_with_malloc.o",
+                         "$COMMON/malloc_wrappers.o"])
+env.RunTest(p)
+
diff --git a/third_party/nanopb/tests/regression/issue_205/size_corruption.c b/third_party/nanopb/tests/regression/issue_205/size_corruption.c
new file mode 100644
index 0000000..08cef45
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_205/size_corruption.c
@@ -0,0 +1,12 @@
+#include "size_corruption.pb.h"
+#include <pb_decode.h>
+
+int main()
+{
+    MainMessage msg = MainMessage_init_zero;
+    msg.bar_count = (pb_size_t)-1;
+    pb_release(MainMessage_fields, &msg);
+    
+    return 0;
+}
+
diff --git a/third_party/nanopb/tests/regression/issue_205/size_corruption.proto b/third_party/nanopb/tests/regression/issue_205/size_corruption.proto
new file mode 100644
index 0000000..6c9c245
--- /dev/null
+++ b/third_party/nanopb/tests/regression/issue_205/size_corruption.proto
@@ -0,0 +1,11 @@
+syntax = "proto2";
+import 'nanopb.proto';
+
+message SubMessage {
+    repeated int32 foo = 1 [(nanopb).type = FT_POINTER];
+}
+
+message MainMessage {
+    repeated SubMessage bar = 1 [(nanopb).max_count = 5];
+}
+
diff --git a/third_party/nanopb/tests/site_scons/site_init.py b/third_party/nanopb/tests/site_scons/site_init.py
new file mode 100644
index 0000000..da5f6d6
--- /dev/null
+++ b/third_party/nanopb/tests/site_scons/site_init.py
@@ -0,0 +1,109 @@
+import subprocess
+import sys
+import re
+
+try:
+    # Make terminal colors work on windows
+    import colorama
+    colorama.init()
+except ImportError:
+    pass
+
+def add_nanopb_builders(env):
+    '''Add the necessary builder commands for nanopb tests.'''
+
+    # Build command that runs a test program and saves the output
+    def run_test(target, source, env):
+        if len(source) > 1:
+            infile = open(str(source[1]))
+        else:
+            infile = None
+        
+        if env.has_key("COMMAND"):
+            args = [env["COMMAND"]]
+        else:
+            args = [str(source[0])]
+        
+        if env.has_key('ARGS'):
+            args.extend(env['ARGS'])
+        
+        print 'Command line: ' + str(args)
+        pipe = subprocess.Popen(args,
+                                stdin = infile,
+                                stdout = open(str(target[0]), 'w'),
+                                stderr = sys.stderr)
+        result = pipe.wait()
+        if result == 0:
+            print '\033[32m[ OK ]\033[0m   Ran ' + args[0]
+        else:
+            print '\033[31m[FAIL]\033[0m   Program ' + args[0] + ' returned ' + str(result)
+        return result
+        
+    run_test_builder = Builder(action = run_test,
+                               suffix = '.output')
+    env.Append(BUILDERS = {'RunTest': run_test_builder})
+
+    # Build command that decodes a message using protoc
+    def decode_actions(source, target, env, for_signature):
+        esc = env['ESCAPE']
+        dirs = ' '.join(['-I' + esc(env.GetBuildPath(d)) for d in env['PROTOCPATH']])
+        return '$PROTOC $PROTOCFLAGS %s --decode=%s %s <%s >%s' % (
+            dirs, env['MESSAGE'], esc(str(source[1])), esc(str(source[0])), esc(str(target[0])))
+
+    decode_builder = Builder(generator = decode_actions,
+                             suffix = '.decoded')
+    env.Append(BUILDERS = {'Decode': decode_builder})    
+
+    # Build command that encodes a message using protoc
+    def encode_actions(source, target, env, for_signature):
+        esc = env['ESCAPE']
+        dirs = ' '.join(['-I' + esc(env.GetBuildPath(d)) for d in env['PROTOCPATH']])
+        return '$PROTOC $PROTOCFLAGS %s --encode=%s %s <%s >%s' % (
+            dirs, env['MESSAGE'], esc(str(source[1])), esc(str(source[0])), esc(str(target[0])))
+
+    encode_builder = Builder(generator = encode_actions,
+                             suffix = '.encoded')
+    env.Append(BUILDERS = {'Encode': encode_builder})    
+
+    # Build command that asserts that two files be equal
+    def compare_files(target, source, env):
+        data1 = open(str(source[0]), 'rb').read()
+        data2 = open(str(source[1]), 'rb').read()
+        if data1 == data2:
+            print '\033[32m[ OK ]\033[0m   Files equal: ' + str(source[0]) + ' and ' + str(source[1])
+            return 0
+        else:
+            print '\033[31m[FAIL]\033[0m   Files differ: ' + str(source[0]) + ' and ' + str(source[1])
+            return 1
+
+    compare_builder = Builder(action = compare_files,
+                              suffix = '.equal')
+    env.Append(BUILDERS = {'Compare': compare_builder})
+
+    # Build command that checks that each pattern in source2 is found in source1.
+    def match_files(target, source, env):
+        data = open(str(source[0]), 'rU').read()
+        patterns = open(str(source[1]))
+        for pattern in patterns:
+            if pattern.strip():
+                invert = False
+                if pattern.startswith('! '):
+                    invert = True
+                    pattern = pattern[2:]
+                
+                status = re.search(pattern.strip(), data, re.MULTILINE)
+                
+                if not status and not invert:
+                    print '\033[31m[FAIL]\033[0m   Pattern not found in ' + str(source[0]) + ': ' + pattern
+                    return 1
+                elif status and invert:
+                    print '\033[31m[FAIL]\033[0m   Pattern should not exist, but does in ' + str(source[0]) + ': ' + pattern
+                    return 1
+        else:
+            print '\033[32m[ OK ]\033[0m   All patterns found in ' + str(source[0])
+            return 0
+
+    match_builder = Builder(action = match_files, suffix = '.matched')
+    env.Append(BUILDERS = {'Match': match_builder})
+    
+
diff --git a/third_party/nanopb/tests/site_scons/site_tools/nanopb.py b/third_party/nanopb/tests/site_scons/site_tools/nanopb.py
new file mode 100644
index 0000000..c72a45d
--- /dev/null
+++ b/third_party/nanopb/tests/site_scons/site_tools/nanopb.py
@@ -0,0 +1,126 @@
+'''
+Scons Builder for nanopb .proto definitions.
+
+This tool will locate the nanopb generator and use it to generate .pb.c and
+.pb.h files from the .proto files.
+
+Basic example
+-------------
+# Build myproto.pb.c and myproto.pb.h from myproto.proto
+myproto = env.NanopbProto("myproto")
+
+# Link nanopb core to the program
+env.Append(CPPPATH = "$NANOB")
+myprog = env.Program(["myprog.c", myproto, "$NANOPB/pb_encode.c", "$NANOPB/pb_decode.c"])
+
+Configuration options
+---------------------
+Normally, this script is used in the test environment of nanopb and it locates
+the nanopb generator by a relative path. If this script is used in another
+application, the path to nanopb root directory has to be defined:
+
+env.SetDefault(NANOPB = "path/to/nanopb")
+
+Additionally, the path to protoc and the options to give to protoc can be
+defined manually:
+
+env.SetDefault(PROTOC = "path/to/protoc")
+env.SetDefault(PROTOCFLAGS = "--plugin=protoc-gen-nanopb=path/to/protoc-gen-nanopb")
+'''
+
+import SCons.Action
+import SCons.Builder
+import SCons.Util
+import os.path
+
+class NanopbWarning(SCons.Warnings.Warning):
+    pass
+SCons.Warnings.enableWarningClass(NanopbWarning)
+
+def _detect_nanopb(env):
+    '''Find the path to nanopb root directory.'''
+    if env.has_key('NANOPB'):
+        # Use nanopb dir given by user
+        return env['NANOPB']
+    
+    p = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
+    if os.path.isdir(p) and os.path.isfile(os.path.join(p, 'pb.h')):
+        # Assume we are running under tests/site_scons/site_tools
+        return p
+    
+    raise SCons.Errors.StopError(NanopbWarning,
+        "Could not find the nanopb root directory")
+
+def _detect_protoc(env):
+    '''Find the path to the protoc compiler.'''
+    if env.has_key('PROTOC'):
+        # Use protoc defined by user
+        return env['PROTOC']
+    
+    n = _detect_nanopb(env)
+    p1 = os.path.join(n, 'generator-bin', 'protoc' + env['PROGSUFFIX'])
+    if os.path.exists(p1):
+        # Use protoc bundled with binary package
+        return env['ESCAPE'](p1)
+    
+    p = env.WhereIs('protoc')
+    if p:
+        # Use protoc from path
+        return env['ESCAPE'](p)
+    
+    raise SCons.Errors.StopError(NanopbWarning,
+        "Could not find the protoc compiler")
+
+def _detect_protocflags(env):
+    '''Find the options to use for protoc.'''
+    if env.has_key('PROTOCFLAGS'):
+        return env['PROTOCFLAGS']
+    
+    p = _detect_protoc(env)
+    n = _detect_nanopb(env)
+    p1 = os.path.join(n, 'generator-bin', 'protoc' + env['PROGSUFFIX'])
+    if p == env['ESCAPE'](p1):
+        # Using the bundled protoc, no options needed
+        return ''
+    
+    e = env['ESCAPE']
+    if env['PLATFORM'] == 'win32':
+        return e('--plugin=protoc-gen-nanopb=' + os.path.join(n, 'generator', 'protoc-gen-nanopb.bat'))
+    else:
+        return e('--plugin=protoc-gen-nanopb=' + os.path.join(n, 'generator', 'protoc-gen-nanopb'))
+
+def _nanopb_proto_actions(source, target, env, for_signature):
+    esc = env['ESCAPE']
+    dirs = ' '.join(['-I' + esc(env.GetBuildPath(d)) for d in env['PROTOCPATH']])
+    return '$PROTOC $PROTOCFLAGS %s --nanopb_out=. %s' % (dirs, esc(str(source[0])))
+
+def _nanopb_proto_emitter(target, source, env):
+    basename = os.path.splitext(str(source[0]))[0]
+    target.append(basename + '.pb.h')
+    
+    if os.path.exists(basename + '.options'):
+        source.append(basename + '.options')
+    
+    return target, source
+
+_nanopb_proto_builder = SCons.Builder.Builder(
+    generator = _nanopb_proto_actions,
+    suffix = '.pb.c',
+    src_suffix = '.proto',
+    emitter = _nanopb_proto_emitter)
+       
+def generate(env):
+    '''Add Builder for nanopb protos.'''
+    
+    env['NANOPB'] = _detect_nanopb(env)
+    env['PROTOC'] = _detect_protoc(env)
+    env['PROTOCFLAGS'] = _detect_protocflags(env)
+    
+    env.SetDefault(PROTOCPATH = ['.', os.path.join(env['NANOPB'], 'generator', 'proto')])
+    
+    env.SetDefault(NANOPB_PROTO_CMD = '$PROTOC $PROTOCFLAGS --nanopb_out=. $SOURCES')
+    env['BUILDERS']['NanopbProto'] = _nanopb_proto_builder
+    
+def exists(env):
+    return _detect_protoc(env) and _detect_protoc_opts(env)
+
diff --git a/third_party/nanopb/tests/special_characters/SConscript b/third_party/nanopb/tests/special_characters/SConscript
new file mode 100644
index 0000000..2309cf2
--- /dev/null
+++ b/third_party/nanopb/tests/special_characters/SConscript
@@ -0,0 +1,6 @@
+# Test that special characters in .proto filenames work.
+
+Import('env')
+
+env.NanopbProto("funny-proto+name has.characters.proto")
+env.Object("funny-proto+name has.characters.pb.c")
diff --git a/third_party/nanopb/tests/special_characters/funny-proto+name has.characters.proto b/third_party/nanopb/tests/special_characters/funny-proto+name has.characters.proto
new file mode 100644
index 0000000..26b2cb1
--- /dev/null
+++ b/third_party/nanopb/tests/special_characters/funny-proto+name has.characters.proto
@@ -0,0 +1 @@
+syntax="proto2";
diff --git a/third_party/nanopb/tests/splint/SConscript b/third_party/nanopb/tests/splint/SConscript
new file mode 100644
index 0000000..cd4b5b9
--- /dev/null
+++ b/third_party/nanopb/tests/splint/SConscript
@@ -0,0 +1,16 @@
+# Check the nanopb core using splint
+
+Import('env')
+
+p = env.WhereIs('splint')
+
+if p:
+    env.Command('pb_decode.splint', '$NANOPB/pb_decode.c',
+        'splint -f splint/splint.rc $SOURCE 2> $TARGET')
+
+    env.Command('pb_encode.splint', '$NANOPB/pb_encode.c',
+        'splint -f splint/splint.rc $SOURCE 2> $TARGET')
+
+    env.Command('pb_common.splint', '$NANOPB/pb_common.c',
+        'splint -f splint/splint.rc $SOURCE 2> $TARGET')
+
diff --git a/third_party/nanopb/tests/splint/splint.rc b/third_party/nanopb/tests/splint/splint.rc
new file mode 100644
index 0000000..e47d3c2
--- /dev/null
+++ b/third_party/nanopb/tests/splint/splint.rc
@@ -0,0 +1,37 @@
++checks
++partial
++matchanyintegral
++strictlib
+-nullassign
+-predboolint
+-predboolptr
++ptrnegate
+-switchloopbreak
++ignoresigns
+-infloopsuncon
+-type
+
+# splint's memory checks don't quite work without annotations
+-mustfreeonly
+-compmempass
+-nullret
+-observertrans
+-statictrans
+-compdestroy
+-nullpass
+-nullstate
+-compdef
+-usereleased
+-temptrans
+-dependenttrans
+-kepttrans
+-branchstate
+-immediatetrans
+-mustfreefresh
+
+# These tests give false positives, compiler typically has
+# better warnings for these.
+-noret
+-noeffect
+-usedef
+
diff --git a/third_party/nanopb/tools/make_linux_package.sh b/third_party/nanopb/tools/make_linux_package.sh
new file mode 100755
index 0000000..6598936
--- /dev/null
+++ b/third_party/nanopb/tools/make_linux_package.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# Run this script in the top nanopb directory to create a binary package
+# for Linux users.
+
+set -e
+set -x
+
+VERSION=`git describe --always`-linux-x86
+DEST=dist/$VERSION
+
+rm -rf $DEST
+mkdir -p $DEST
+
+# Export the files from newest commit
+git archive HEAD | tar x -C $DEST
+
+# Rebuild the Python .proto files
+make -BC $DEST/generator/proto
+
+# Make the nanopb generator available as a protoc plugin
+cp $DEST/generator/nanopb_generator.py $DEST/generator/protoc-gen-nanopb.py
+
+# Package the Python libraries
+( cd $DEST/generator; bbfreeze nanopb_generator.py protoc-gen-nanopb.py )
+mv $DEST/generator/dist $DEST/generator-bin
+
+# Remove temp file
+rm $DEST/generator/protoc-gen-nanopb.py
+
+# Package the protoc compiler
+cp `which protoc` $DEST/generator-bin/protoc.bin
+LIBPROTOC=$(ldd `which protoc` | grep -o '/.*libprotoc[^ ]*')
+LIBPROTOBUF=$(ldd `which protoc` | grep -o '/.*libprotobuf[^ ]*')
+cp $LIBPROTOC $LIBPROTOBUF $DEST/generator-bin/
+cat > $DEST/generator-bin/protoc << EOF
+#!/bin/bash
+SCRIPTDIR=\$(dirname "\$0")
+export LD_LIBRARY_PATH=\$SCRIPTDIR
+export PATH=\$SCRIPTDIR:\$PATH
+exec "\$SCRIPTDIR/protoc.bin" "\$@"
+EOF
+chmod +x $DEST/generator-bin/protoc
+
+# Tar it all up
+( cd dist; tar -czf $VERSION.tar.gz $VERSION )
+
diff --git a/third_party/nanopb/tools/make_mac_package.sh b/third_party/nanopb/tools/make_mac_package.sh
new file mode 100755
index 0000000..32bba5c
--- /dev/null
+++ b/third_party/nanopb/tools/make_mac_package.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Run this script in the top nanopb directory to create a binary package
+# for Mac OS X users.
+
+# Requires: protobuf, python-protobuf, pyinstaller
+
+set -e
+set -x
+
+VERSION=`git describe --always`-macosx-x86
+DEST=dist/$VERSION
+
+rm -rf $DEST
+mkdir -p $DEST
+
+# Export the files from newest commit
+git archive HEAD | tar x -C $DEST
+
+# Rebuild the Python .proto files
+make -BC $DEST/generator/proto
+
+# Package the Python libraries
+( cd $DEST/generator; pyinstaller nanopb_generator.py )
+mv $DEST/generator/dist/nanopb_generator $DEST/generator-bin
+
+# Remove temp files
+rm -rf $DEST/generator/dist $DEST/generator/build $DEST/generator/nanopb_generator.spec
+
+# Make the nanopb generator available as a protoc plugin
+cp $DEST/generator-bin/nanopb_generator $DEST/generator-bin/protoc-gen-nanopb
+
+# Package the protoc compiler
+cp `which protoc` $DEST/generator-bin/protoc.bin
+LIBPROTOC=$(otool -L `which protoc` | grep -o '/.*libprotoc[^ ]*')
+LIBPROTOBUF=$(otool -L `which protoc` | grep -o '/.*libprotobuf[^ ]*')
+cp $LIBPROTOC $LIBPROTOBUF $DEST/generator-bin/
+cat > $DEST/generator-bin/protoc << EOF
+#!/bin/bash
+SCRIPTDIR=\$(dirname "\$0")
+export DYLD_LIBRARY_PATH=\$SCRIPTDIR
+export PATH=\$SCRIPTDIR:\$PATH
+exec "\$SCRIPTDIR/protoc.bin" "\$@"
+EOF
+chmod +x $DEST/generator-bin/protoc
+
+# Tar it all up
+( cd dist; tar -czf $VERSION.tar.gz $VERSION )
+
diff --git a/third_party/nanopb/tools/make_windows_package.sh b/third_party/nanopb/tools/make_windows_package.sh
new file mode 100755
index 0000000..72de6f3
--- /dev/null
+++ b/third_party/nanopb/tools/make_windows_package.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+# Run this script in the top nanopb directory to create a binary package
+# for Windows users. This script is designed to run under MingW/MSYS bash
+# and requires the following tools: git, make, zip, unix2dos
+
+set -e
+set -x
+
+VERSION=`git describe --always`-windows-x86
+DEST=dist/$VERSION
+
+rm -rf $DEST
+mkdir -p $DEST
+
+# Export the files from newest commit
+git archive HEAD | tar x -C $DEST
+
+# Rebuild the Python .proto files
+make -BC $DEST/generator/proto
+
+# Make the nanopb generator available as a protoc plugin
+cp $DEST/generator/nanopb_generator.py $DEST/generator/protoc-gen-nanopb.py
+
+# Package the Python libraries
+( cd $DEST/generator; bbfreeze nanopb_generator.py protoc-gen-nanopb.py )
+mv $DEST/generator/dist $DEST/generator-bin
+
+# Remove temp file
+rm $DEST/generator/protoc-gen-nanopb.py
+
+# The python interpreter requires MSVCR90.dll.
+# FIXME: Find a way around hardcoding this path
+cp /c/windows/winsxs/x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.4974_none_50940634bcb759cb/MSVCR90.DLL $DEST/generator-bin/
+cat > $DEST/generator-bin/Microsoft.VC90.CRT.manifest <<EOF
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+    <noInheritable></noInheritable>
+    <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
+    <file name="msvcr90.dll" hashalg="SHA1" hash="e0dcdcbfcb452747da530fae6b000d47c8674671"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>KSaO8M0iCtPF6YEr79P1dZsnomY=</dsig:DigestValue></asmv2:hash></file> <file name="msvcp90.dll" hashalg="SHA1" hash="81efe890e4ef2615c0bb4dda7b94bea177c86ebd"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>ojDmTgpYMFRKJYkPcM6ckpYkWUU=</dsig:DigestValue></asmv2:hash></file> <file name="msvcm90.dll" hashalg="SHA1" hash="5470081b336abd7b82c6387567a661a729483b04"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>tVogb8kezDre2mXShlIqpp8ErIg=</dsig:DigestValue></asmv2:hash></file>
+</assembly>
+EOF
+
+# Package the protoc compiler
+cp `which protoc.exe` $DEST/generator-bin/
+cp `which MSVCR100.DLL` $DEST/generator-bin/
+cp `which MSVCP100.DLL` $DEST/generator-bin/
+
+# Convert line breaks for convenience
+find $DEST -name '*.c' -o -name '*.h' -o -name '*.txt' \
+    -o -name '*.proto' -o -name '*.py' -o -name '*.options' \
+    -exec unix2dos '{}' \;
+
+# Zip it all up
+( cd dist; zip -r $VERSION.zip $VERSION )
diff --git a/third_party/nanopb/tools/set_version.sh b/third_party/nanopb/tools/set_version.sh
new file mode 100755
index 0000000..e15a859
--- /dev/null
+++ b/third_party/nanopb/tools/set_version.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Run this from the top directory of nanopb tree.
+# e.g. user@localhost:~/nanopb$ tools/set_version.sh nanopb-0.1.9-dev
+# It sets the version number in pb.h and generator/nanopb_generator.py.
+
+sed -i -e 's/nanopb_version\s*=\s*"[^"]*"/nanopb_version = "'$1'"/' generator/nanopb_generator.py
+sed -i -e 's/#define\s*NANOPB_VERSION\s*.*/#define NANOPB_VERSION '$1'/' pb.h
+
+
diff --git a/third_party/protobuf b/third_party/protobuf
index bdeb215..a428e42 160000
--- a/third_party/protobuf
+++ b/third_party/protobuf
@@ -1 +1 @@
-Subproject commit bdeb215cab2985195325fcd5e70c3fa751f46e0f
+Subproject commit a428e42072765993ff674fda72863c9f1aa2d268
diff --git a/third_party/thrift b/third_party/thrift
new file mode 160000
index 0000000..bcad917
--- /dev/null
+++ b/third_party/thrift
@@ -0,0 +1 @@
+Subproject commit bcad91771b7f0bff28a1cac1981d7ef2b9bcef3c
diff --git a/tools/buildgen/plugins/expand_version.py b/tools/buildgen/plugins/expand_version.py
index c6cc562..6098cca 100755
--- a/tools/buildgen/plugins/expand_version.py
+++ b/tools/buildgen/plugins/expand_version.py
@@ -85,10 +85,21 @@
       return '%d.%d.%d' % (self.major, self.minor, self.patch)
 
   def php(self):
-    """Version string in PHP style"""
-    """PECL does not allow tag in version string"""
-    return '%d.%d.%d' % (self.major, self.minor, self.patch)
+    """Version string for PHP PECL package"""
+    s = '%d.%d.%d' % (self.major, self.minor, self.patch)
+    if self.tag:
+      if self.tag == 'dev':
+        s += 'dev'
+      elif len(self.tag) >= 3 and self.tag[0:3] == 'pre':
+        s += 'RC%d' % int(self.tag[3:])
+      else:
+        raise Exception('Don\'t know how to translate version tag "%s" to PECL version' % self.tag)
+    return s
 
+  def php_composer(self):
+    """Version string for PHP Composer package"""
+    return '%d.%d.%d' % (self.major, self.minor, self.patch)
+    
 def mako_plugin(dictionary):
   """Expand version numbers:
      - for each language, ensure there's a language_version tag in
diff --git a/tools/codegen/core/gen_nano_proto.sh b/tools/codegen/core/gen_nano_proto.sh
index c880fc2..df107c2 100755
--- a/tools/codegen/core/gen_nano_proto.sh
+++ b/tools/codegen/core/gen_nano_proto.sh
@@ -123,7 +123,7 @@
 
 # this should be the same version as the submodule we compile against
 # ideally we'd update this as a template to ensure that
-pip install protobuf==3.0.0b2
+pip install protobuf==3.0.0
 
 pushd "$(dirname $INPUT_PROTO)" > /dev/null
 
diff --git a/tools/codegen/core/gen_percent_encoding_tables.c b/tools/codegen/core/gen_percent_encoding_tables.c
new file mode 100644
index 0000000..93f30de
--- /dev/null
+++ b/tools/codegen/core/gen_percent_encoding_tables.c
@@ -0,0 +1,84 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* generates constant table for metadata.c */
+
+#include <stdio.h>
+#include <string.h>
+
+static unsigned char legal_bits[256 / 8];
+
+static void legal(int x) {
+  int byte = x / 8;
+  int bit = x % 8;
+  /* NB: the following integer arithmetic operation needs to be in its
+   * expanded form due to the "integral promotion" performed (see section
+   * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type
+   * is then required to avoid the compiler warning */
+  legal_bits[byte] =
+      (unsigned char)((legal_bits[byte] | (unsigned char)(1 << bit)));
+}
+
+static void dump(const char *name) {
+  int i;
+
+  printf("const uint8_t %s[256/8] = ", name);
+  for (i = 0; i < 256 / 8; i++)
+    printf("%c 0x%02x", i ? ',' : '{', legal_bits[i]);
+  printf(" };\n");
+}
+
+static void clear(void) { memset(legal_bits, 0, sizeof(legal_bits)); }
+
+int main(void) {
+  int i;
+
+  clear();
+  for (i = 'a'; i <= 'z'; i++) legal(i);
+  for (i = 'A'; i <= 'Z'; i++) legal(i);
+  for (i = '0'; i <= '9'; i++) legal(i);
+  legal('-');
+  legal('_');
+  legal('.');
+  legal('~');
+  dump("gpr_url_percent_encoding_unreserved_bytes");
+
+  clear();
+  for (i = 32; i <= 126; i++) {
+    if (i == '%') continue;
+    legal(i);
+  }
+  dump("gpr_compatible_percent_encoding_unreserved_bytes");
+
+  return 0;
+}
diff --git a/tools/codegen/core/gen_static_metadata.py b/tools/codegen/core/gen_static_metadata.py
index faa8386..6f40010 100755
--- a/tools/codegen/core/gen_static_metadata.py
+++ b/tools/codegen/core/gen_static_metadata.py
@@ -42,6 +42,7 @@
 CONFIG = [
     'grpc-timeout',
     'grpc-internal-encoding-request',
+    'grpc-payload-bin',
     ':path',
     'grpc-encoding',
     'grpc-accept-encoding',
@@ -50,8 +51,8 @@
     'host',
     'grpc-message',
     'grpc-status',
-    'census-bin',
-    'census-binary-bin',
+    'grpc-tracing-bin',
+    'grpc-stats-bin',
     '',
     ('grpc-status', '0'),
     ('grpc-status', '1'),
@@ -108,7 +109,8 @@
     ('if-range', ''),
     ('if-unmodified-since', ''),
     ('last-modified', ''),
-    ('load-reporting', ''),
+    ('load-reporting-initial', ''),
+    ('load-reporting-trailing', ''),
     ('link', ''),
     ('location', ''),
     ('max-forwards', ''),
diff --git a/tools/codegen/extensions/gen_reflection_proto.sh b/tools/codegen/extensions/gen_reflection_proto.sh
index 45a1a9f..ea7689f 100755
--- a/tools/codegen/extensions/gen_reflection_proto.sh
+++ b/tools/codegen/extensions/gen_reflection_proto.sh
@@ -29,21 +29,40 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+set -e
+cd $(dirname $0)/../../..
+
 PROTO_DIR="src/proto/grpc/reflection/v1alpha"
 PROTO_FILE="reflection"
 HEADER_DIR="include/grpc++/ext"
 SRC_DIR="src/cpp/ext"
 INCLUDE_DIR="grpc++/ext"
 TMP_DIR="tmp"
-GRPC_PLUGIN="bins/opt/grpc_cpp_plugin"
-PROTOC=third_party/protobuf/src/protoc
 
-set -e
+if hash grpc_cpp_plugin 2>/dev/null; then
+  GRPC_PLUGIN=$(which grpc_cpp_plugin)
+else
+  if [ -f bins/opt/grpc_cpp_plugin ]; then
+    GRPC_PLUGIN="bins/opt/grpc_cpp_plugin"
+  else
+    echo "gRPC protoc plugin not found"
+    exit 1
+  fi
+fi
+
+if hash protoc 2>/dev/null; then
+  PROTOC=$(which protoc)
+else
+  if [ -f bins/opt/protobuf/protoc ]; then
+    PROTOC="bins/opt/protobuf/protoc"
+  else
+    echo "protoc not found"
+    exit 1
+  fi
+fi
 
 TMP_DIR=${TMP_DIR}_${PROTO_FILE}
 
-cd $(dirname $0)/../../..
-
 [ ! -d $HEADER_DIR ] && mkdir -p $HEADER_DIR || :
 [ ! -d $SRC_DIR ] && mkdir -p $SRC_DIR || :
 [ ! -d $TMP_DIR ] && mkdir -p $TMP_DIR || :
@@ -56,6 +75,9 @@
 sed -i "s/\"${PROTO_FILE}.pb.h\"/<${INCLUDE_DIR/\//\\\/}\/${PROTO_FILE}.pb.h>/g" ${TMP_DIR}/${PROTO_FILE}.grpc.pb.cc
 sed -i "s/\"${PROTO_FILE}.grpc.pb.h\"/<${INCLUDE_DIR/\//\\\/}\/${PROTO_FILE}.grpc.pb.h>/g" ${TMP_DIR}/${PROTO_FILE}.grpc.pb.cc
 
+sed -i "1s/.*/\/\/ Generated by tools\/codegen\/extensions\/gen_reflection_proto.sh/g" ${TMP_DIR}/*.pb.h
+sed -i "1s/.*/\/\/ Generated by tools\/codegen\/extensions\/gen_reflection_proto.sh/g" ${TMP_DIR}/*.pb.cc
+
 /bin/cp LICENSE ${TMP_DIR}/TMP_LICENSE
 sed -i -e "s/./ &/" -e "s/.*/ \*&/" ${TMP_DIR}/TMP_LICENSE
 sed -i -r "\$a\ *\n *\/\n\n"  ${TMP_DIR}/TMP_LICENSE
diff --git a/tools/distrib/c-ish/check_documentation.py b/tools/distrib/c-ish/check_documentation.py
new file mode 100755
index 0000000..0bb3ca6
--- /dev/null
+++ b/tools/distrib/c-ish/check_documentation.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python2.7
+
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# check for directory level 'README.md' files
+# check that all implementation and interface files have a \file doxygen comment
+
+import os
+import sys
+
+# where do we run
+_TARGET_DIRS = [
+  'include/grpc',
+  'include/grpc++',
+  'src/core',
+  'src/cpp',
+  'test/core',
+  'test/cpp'
+]
+
+# which file extensions do we care about
+_INTERESTING_EXTENSIONS = [
+  '.c',
+  '.h',
+  '.cc'
+]
+
+# find our home
+_ROOT = os.path.abspath(
+    os.path.join(os.path.dirname(sys.argv[0]), '../../..'))
+os.chdir(_ROOT)
+
+errors = 0
+
+# walk directories, find things
+printed_banner = False
+for target_dir in _TARGET_DIRS:
+  for root, dirs, filenames in os.walk(target_dir):
+    if 'README.md' not in filenames:
+      if not printed_banner:
+        print 'Missing README.md'
+        print '================='
+        printed_banner = True
+      print root
+      errors += 1
+if printed_banner: print
+printed_banner = False
+for target_dir in _TARGET_DIRS:
+  for root, dirs, filenames in os.walk(target_dir):
+    for filename in filenames:
+      if os.path.splitext(filename)[1] not in _INTERESTING_EXTENSIONS:
+        continue
+      path = os.path.join(root, filename)
+      with open(path) as f:
+        contents = f.read()
+      if '\\file' not in contents:
+        if not printed_banner:
+          print 'Missing \\file comment'
+          print '======================'
+          printed_banner = True
+        print path
+        errors += 1
+
+assert errors == 0, 'error count = %d' % errors
diff --git a/tools/distrib/check_copyright.py b/tools/distrib/check_copyright.py
index 4577ab3..f06e5f1 100755
--- a/tools/distrib/check_copyright.py
+++ b/tools/distrib/check_copyright.py
@@ -157,7 +157,7 @@
   m = re.search(re_license, text)
   if m:
     pass
-  elif 'DO NOT EDIT' not in text and 'AssemblyInfo.cs' not in filename and filename != 'src/boringssl/err_data.c':
+  elif 'DO NOT EDIT' not in text and filename != 'src/boringssl/err_data.c':
     log(1, 'copyright missing', filename)
     ok = False
 
diff --git a/tools/distrib/check_generated_pb_files.sh b/tools/distrib/check_generated_pb_files.sh
index 5570678..6b93895 100755
--- a/tools/distrib/check_generated_pb_files.sh
+++ b/tools/distrib/check_generated_pb_files.sh
@@ -38,3 +38,6 @@
 
 # run check_pb_files against the checked out codebase
 docker run -e TEST=$TEST --rm=true -v ${HOST_GIT_ROOT:-`pwd`}:/var/local/jenkins/grpc -t grpc_check_generated_pb_files /var/local/jenkins/grpc/tools/dockerfile/grpc_check_generated_pb_files/check_pb_files.sh
+
+# If the test fails, please make sure your protobuf submodule is up-to-date and run
+# tools/codegen/extensions/gen_reflection_proto.sh to update the generated files.
diff --git a/tools/distrib/check_include_guards.py b/tools/distrib/check_include_guards.py
index 56b2924..2831281 100755
--- a/tools/distrib/check_include_guards.py
+++ b/tools/distrib/check_include_guards.py
@@ -200,6 +200,6 @@
 
 for filename in filename_list:
   if filename in KNOWN_BAD: continue
-  ok = validator.check(filename, args.fix)
+  ok = ok and validator.check(filename, args.fix)
 
 sys.exit(0 if ok else 1)
diff --git a/tools/distrib/python/grpcio_tools/protoc_lib_deps.py b/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
index cd4effa..569328e 100644
--- a/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
+++ b/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
@@ -29,7 +29,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 # AUTO-GENERATED BY make_grpcio_tools.py!
-CC_FILES=['google/protobuf/compiler/zip_writer.cc', 'google/protobuf/compiler/subprocess.cc', 'google/protobuf/compiler/ruby/ruby_generator.cc', 'google/protobuf/compiler/python/python_generator.cc', 'google/protobuf/compiler/plugin.pb.cc', 'google/protobuf/compiler/plugin.cc', 'google/protobuf/compiler/objectivec/objectivec_primitive_field.cc', 'google/protobuf/compiler/objectivec/objectivec_oneof.cc', 'google/protobuf/compiler/objectivec/objectivec_message_field.cc', 'google/protobuf/compiler/objectivec/objectivec_message.cc', 'google/protobuf/compiler/objectivec/objectivec_map_field.cc', 'google/protobuf/compiler/objectivec/objectivec_helpers.cc', 'google/protobuf/compiler/objectivec/objectivec_generator.cc', 'google/protobuf/compiler/objectivec/objectivec_file.cc', 'google/protobuf/compiler/objectivec/objectivec_field.cc', 'google/protobuf/compiler/objectivec/objectivec_extension.cc', 'google/protobuf/compiler/objectivec/objectivec_enum_field.cc', 'google/protobuf/compiler/objectivec/objectivec_enum.cc', 'google/protobuf/compiler/js/js_generator.cc', 'google/protobuf/compiler/javanano/javanano_primitive_field.cc', 'google/protobuf/compiler/javanano/javanano_message_field.cc', 'google/protobuf/compiler/javanano/javanano_message.cc', 'google/protobuf/compiler/javanano/javanano_map_field.cc', 'google/protobuf/compiler/javanano/javanano_helpers.cc', 'google/protobuf/compiler/javanano/javanano_generator.cc', 'google/protobuf/compiler/javanano/javanano_file.cc', 'google/protobuf/compiler/javanano/javanano_field.cc', 'google/protobuf/compiler/javanano/javanano_extension.cc', 'google/protobuf/compiler/javanano/javanano_enum_field.cc', 'google/protobuf/compiler/javanano/javanano_enum.cc', 'google/protobuf/compiler/java/java_string_field_lite.cc', 'google/protobuf/compiler/java/java_string_field.cc', 'google/protobuf/compiler/java/java_shared_code_generator.cc', 'google/protobuf/compiler/java/java_service.cc', 'google/protobuf/compiler/java/java_primitive_field_lite.cc', 'google/protobuf/compiler/java/java_primitive_field.cc', 'google/protobuf/compiler/java/java_name_resolver.cc', 'google/protobuf/compiler/java/java_message_lite.cc', 'google/protobuf/compiler/java/java_message_field_lite.cc', 'google/protobuf/compiler/java/java_message_field.cc', 'google/protobuf/compiler/java/java_message_builder_lite.cc', 'google/protobuf/compiler/java/java_message_builder.cc', 'google/protobuf/compiler/java/java_message.cc', 'google/protobuf/compiler/java/java_map_field_lite.cc', 'google/protobuf/compiler/java/java_map_field.cc', 'google/protobuf/compiler/java/java_lazy_message_field_lite.cc', 'google/protobuf/compiler/java/java_lazy_message_field.cc', 'google/protobuf/compiler/java/java_helpers.cc', 'google/protobuf/compiler/java/java_generator_factory.cc', 'google/protobuf/compiler/java/java_generator.cc', 'google/protobuf/compiler/java/java_file.cc', 'google/protobuf/compiler/java/java_field.cc', 'google/protobuf/compiler/java/java_extension_lite.cc', 'google/protobuf/compiler/java/java_extension.cc', 'google/protobuf/compiler/java/java_enum_lite.cc', 'google/protobuf/compiler/java/java_enum_field_lite.cc', 'google/protobuf/compiler/java/java_enum_field.cc', 'google/protobuf/compiler/java/java_enum.cc', 'google/protobuf/compiler/java/java_doc_comment.cc', 'google/protobuf/compiler/java/java_context.cc', 'google/protobuf/compiler/csharp/csharp_wrapper_field.cc', 'google/protobuf/compiler/csharp/csharp_source_generator_base.cc', 'google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc', 'google/protobuf/compiler/csharp/csharp_repeated_message_field.cc', 'google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc', 'google/protobuf/compiler/csharp/csharp_reflection_class.cc', 'google/protobuf/compiler/csharp/csharp_primitive_field.cc', 'google/protobuf/compiler/csharp/csharp_message_field.cc', 'google/protobuf/compiler/csharp/csharp_message.cc', 'google/protobuf/compiler/csharp/csharp_map_field.cc', 'google/protobuf/compiler/csharp/csharp_helpers.cc', 'google/protobuf/compiler/csharp/csharp_generator.cc', 'google/protobuf/compiler/csharp/csharp_field_base.cc', 'google/protobuf/compiler/csharp/csharp_enum_field.cc', 'google/protobuf/compiler/csharp/csharp_enum.cc', 'google/protobuf/compiler/csharp/csharp_doc_comment.cc', 'google/protobuf/compiler/cpp/cpp_string_field.cc', 'google/protobuf/compiler/cpp/cpp_service.cc', 'google/protobuf/compiler/cpp/cpp_primitive_field.cc', 'google/protobuf/compiler/cpp/cpp_message_field.cc', 'google/protobuf/compiler/cpp/cpp_message.cc', 'google/protobuf/compiler/cpp/cpp_map_field.cc', 'google/protobuf/compiler/cpp/cpp_helpers.cc', 'google/protobuf/compiler/cpp/cpp_generator.cc', 'google/protobuf/compiler/cpp/cpp_file.cc', 'google/protobuf/compiler/cpp/cpp_field.cc', 'google/protobuf/compiler/cpp/cpp_extension.cc', 'google/protobuf/compiler/cpp/cpp_enum_field.cc', 'google/protobuf/compiler/cpp/cpp_enum.cc', 'google/protobuf/compiler/command_line_interface.cc', 'google/protobuf/compiler/code_generator.cc', 'google/protobuf/wrappers.pb.cc', 'google/protobuf/wire_format.cc', 'google/protobuf/util/type_resolver_util.cc', 'google/protobuf/util/time_util.cc', 'google/protobuf/util/message_differencer.cc', 'google/protobuf/util/json_util.cc', 'google/protobuf/util/internal/utility.cc', 'google/protobuf/util/internal/type_info_test_helper.cc', 'google/protobuf/util/internal/type_info.cc', 'google/protobuf/util/internal/protostream_objectwriter.cc', 'google/protobuf/util/internal/protostream_objectsource.cc', 'google/protobuf/util/internal/proto_writer.cc', 'google/protobuf/util/internal/object_writer.cc', 'google/protobuf/util/internal/json_stream_parser.cc', 'google/protobuf/util/internal/json_objectwriter.cc', 'google/protobuf/util/internal/json_escaping.cc', 'google/protobuf/util/internal/field_mask_utility.cc', 'google/protobuf/util/internal/error_listener.cc', 'google/protobuf/util/internal/default_value_objectwriter.cc', 'google/protobuf/util/internal/datapiece.cc', 'google/protobuf/util/field_mask_util.cc', 'google/protobuf/util/field_comparator.cc', 'google/protobuf/unknown_field_set.cc', 'google/protobuf/type.pb.cc', 'google/protobuf/timestamp.pb.cc', 'google/protobuf/text_format.cc', 'google/protobuf/stubs/substitute.cc', 'google/protobuf/stubs/mathlimits.cc', 'google/protobuf/struct.pb.cc', 'google/protobuf/source_context.pb.cc', 'google/protobuf/service.cc', 'google/protobuf/reflection_ops.cc', 'google/protobuf/message.cc', 'google/protobuf/map_field.cc', 'google/protobuf/io/zero_copy_stream_impl.cc', 'google/protobuf/io/tokenizer.cc', 'google/protobuf/io/strtod.cc', 'google/protobuf/io/printer.cc', 'google/protobuf/io/gzip_stream.cc', 'google/protobuf/generated_message_reflection.cc', 'google/protobuf/field_mask.pb.cc', 'google/protobuf/extension_set_heavy.cc', 'google/protobuf/empty.pb.cc', 'google/protobuf/dynamic_message.cc', 'google/protobuf/duration.pb.cc', 'google/protobuf/descriptor_database.cc', 'google/protobuf/descriptor.pb.cc', 'google/protobuf/descriptor.cc', 'google/protobuf/compiler/parser.cc', 'google/protobuf/compiler/importer.cc', 'google/protobuf/api.pb.cc', 'google/protobuf/any.pb.cc', 'google/protobuf/any.cc', 'google/protobuf/wire_format_lite.cc', 'google/protobuf/stubs/time.cc', 'google/protobuf/stubs/strutil.cc', 'google/protobuf/stubs/structurally_valid.cc', 'google/protobuf/stubs/stringprintf.cc', 'google/protobuf/stubs/stringpiece.cc', 'google/protobuf/stubs/statusor.cc', 'google/protobuf/stubs/status.cc', 'google/protobuf/stubs/once.cc', 'google/protobuf/stubs/int128.cc', 'google/protobuf/stubs/common.cc', 'google/protobuf/stubs/bytestream.cc', 'google/protobuf/stubs/atomicops_internals_x86_msvc.cc', 'google/protobuf/stubs/atomicops_internals_x86_gcc.cc', 'google/protobuf/repeated_field.cc', 'google/protobuf/message_lite.cc', 'google/protobuf/io/zero_copy_stream_impl_lite.cc', 'google/protobuf/io/zero_copy_stream.cc', 'google/protobuf/io/coded_stream.cc', 'google/protobuf/generated_message_util.cc', 'google/protobuf/extension_set.cc', 'google/protobuf/arenastring.cc', 'google/protobuf/arena.cc']
+CC_FILES=['google/protobuf/compiler/zip_writer.cc', 'google/protobuf/compiler/subprocess.cc', 'google/protobuf/compiler/ruby/ruby_generator.cc', 'google/protobuf/compiler/python/python_generator.cc', 'google/protobuf/compiler/plugin.pb.cc', 'google/protobuf/compiler/plugin.cc', 'google/protobuf/compiler/php/php_generator.cc', 'google/protobuf/compiler/objectivec/objectivec_primitive_field.cc', 'google/protobuf/compiler/objectivec/objectivec_oneof.cc', 'google/protobuf/compiler/objectivec/objectivec_message_field.cc', 'google/protobuf/compiler/objectivec/objectivec_message.cc', 'google/protobuf/compiler/objectivec/objectivec_map_field.cc', 'google/protobuf/compiler/objectivec/objectivec_helpers.cc', 'google/protobuf/compiler/objectivec/objectivec_generator.cc', 'google/protobuf/compiler/objectivec/objectivec_file.cc', 'google/protobuf/compiler/objectivec/objectivec_field.cc', 'google/protobuf/compiler/objectivec/objectivec_extension.cc', 'google/protobuf/compiler/objectivec/objectivec_enum_field.cc', 'google/protobuf/compiler/objectivec/objectivec_enum.cc', 'google/protobuf/compiler/js/js_generator.cc', 'google/protobuf/compiler/javanano/javanano_primitive_field.cc', 'google/protobuf/compiler/javanano/javanano_message_field.cc', 'google/protobuf/compiler/javanano/javanano_message.cc', 'google/protobuf/compiler/javanano/javanano_map_field.cc', 'google/protobuf/compiler/javanano/javanano_helpers.cc', 'google/protobuf/compiler/javanano/javanano_generator.cc', 'google/protobuf/compiler/javanano/javanano_file.cc', 'google/protobuf/compiler/javanano/javanano_field.cc', 'google/protobuf/compiler/javanano/javanano_extension.cc', 'google/protobuf/compiler/javanano/javanano_enum_field.cc', 'google/protobuf/compiler/javanano/javanano_enum.cc', 'google/protobuf/compiler/java/java_string_field_lite.cc', 'google/protobuf/compiler/java/java_string_field.cc', 'google/protobuf/compiler/java/java_shared_code_generator.cc', 'google/protobuf/compiler/java/java_service.cc', 'google/protobuf/compiler/java/java_primitive_field_lite.cc', 'google/protobuf/compiler/java/java_primitive_field.cc', 'google/protobuf/compiler/java/java_name_resolver.cc', 'google/protobuf/compiler/java/java_message_lite.cc', 'google/protobuf/compiler/java/java_message_field_lite.cc', 'google/protobuf/compiler/java/java_message_field.cc', 'google/protobuf/compiler/java/java_message_builder_lite.cc', 'google/protobuf/compiler/java/java_message_builder.cc', 'google/protobuf/compiler/java/java_message.cc', 'google/protobuf/compiler/java/java_map_field_lite.cc', 'google/protobuf/compiler/java/java_map_field.cc', 'google/protobuf/compiler/java/java_lazy_message_field_lite.cc', 'google/protobuf/compiler/java/java_lazy_message_field.cc', 'google/protobuf/compiler/java/java_helpers.cc', 'google/protobuf/compiler/java/java_generator_factory.cc', 'google/protobuf/compiler/java/java_generator.cc', 'google/protobuf/compiler/java/java_file.cc', 'google/protobuf/compiler/java/java_field.cc', 'google/protobuf/compiler/java/java_extension_lite.cc', 'google/protobuf/compiler/java/java_extension.cc', 'google/protobuf/compiler/java/java_enum_lite.cc', 'google/protobuf/compiler/java/java_enum_field_lite.cc', 'google/protobuf/compiler/java/java_enum_field.cc', 'google/protobuf/compiler/java/java_enum.cc', 'google/protobuf/compiler/java/java_doc_comment.cc', 'google/protobuf/compiler/java/java_context.cc', 'google/protobuf/compiler/csharp/csharp_wrapper_field.cc', 'google/protobuf/compiler/csharp/csharp_source_generator_base.cc', 'google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc', 'google/protobuf/compiler/csharp/csharp_repeated_message_field.cc', 'google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc', 'google/protobuf/compiler/csharp/csharp_reflection_class.cc', 'google/protobuf/compiler/csharp/csharp_primitive_field.cc', 'google/protobuf/compiler/csharp/csharp_message_field.cc', 'google/protobuf/compiler/csharp/csharp_message.cc', 'google/protobuf/compiler/csharp/csharp_map_field.cc', 'google/protobuf/compiler/csharp/csharp_helpers.cc', 'google/protobuf/compiler/csharp/csharp_generator.cc', 'google/protobuf/compiler/csharp/csharp_field_base.cc', 'google/protobuf/compiler/csharp/csharp_enum_field.cc', 'google/protobuf/compiler/csharp/csharp_enum.cc', 'google/protobuf/compiler/csharp/csharp_doc_comment.cc', 'google/protobuf/compiler/cpp/cpp_string_field.cc', 'google/protobuf/compiler/cpp/cpp_service.cc', 'google/protobuf/compiler/cpp/cpp_primitive_field.cc', 'google/protobuf/compiler/cpp/cpp_message_field.cc', 'google/protobuf/compiler/cpp/cpp_message.cc', 'google/protobuf/compiler/cpp/cpp_map_field.cc', 'google/protobuf/compiler/cpp/cpp_helpers.cc', 'google/protobuf/compiler/cpp/cpp_generator.cc', 'google/protobuf/compiler/cpp/cpp_file.cc', 'google/protobuf/compiler/cpp/cpp_field.cc', 'google/protobuf/compiler/cpp/cpp_extension.cc', 'google/protobuf/compiler/cpp/cpp_enum_field.cc', 'google/protobuf/compiler/cpp/cpp_enum.cc', 'google/protobuf/compiler/command_line_interface.cc', 'google/protobuf/compiler/code_generator.cc', 'google/protobuf/wrappers.pb.cc', 'google/protobuf/wire_format.cc', 'google/protobuf/util/type_resolver_util.cc', 'google/protobuf/util/time_util.cc', 'google/protobuf/util/message_differencer.cc', 'google/protobuf/util/json_util.cc', 'google/protobuf/util/internal/utility.cc', 'google/protobuf/util/internal/type_info_test_helper.cc', 'google/protobuf/util/internal/type_info.cc', 'google/protobuf/util/internal/protostream_objectwriter.cc', 'google/protobuf/util/internal/protostream_objectsource.cc', 'google/protobuf/util/internal/proto_writer.cc', 'google/protobuf/util/internal/object_writer.cc', 'google/protobuf/util/internal/json_stream_parser.cc', 'google/protobuf/util/internal/json_objectwriter.cc', 'google/protobuf/util/internal/json_escaping.cc', 'google/protobuf/util/internal/field_mask_utility.cc', 'google/protobuf/util/internal/error_listener.cc', 'google/protobuf/util/internal/default_value_objectwriter.cc', 'google/protobuf/util/internal/datapiece.cc', 'google/protobuf/util/field_mask_util.cc', 'google/protobuf/util/field_comparator.cc', 'google/protobuf/unknown_field_set.cc', 'google/protobuf/type.pb.cc', 'google/protobuf/timestamp.pb.cc', 'google/protobuf/text_format.cc', 'google/protobuf/stubs/substitute.cc', 'google/protobuf/stubs/mathlimits.cc', 'google/protobuf/struct.pb.cc', 'google/protobuf/source_context.pb.cc', 'google/protobuf/service.cc', 'google/protobuf/reflection_ops.cc', 'google/protobuf/message.cc', 'google/protobuf/map_field.cc', 'google/protobuf/io/zero_copy_stream_impl.cc', 'google/protobuf/io/tokenizer.cc', 'google/protobuf/io/strtod.cc', 'google/protobuf/io/printer.cc', 'google/protobuf/io/gzip_stream.cc', 'google/protobuf/generated_message_reflection.cc', 'google/protobuf/field_mask.pb.cc', 'google/protobuf/extension_set_heavy.cc', 'google/protobuf/empty.pb.cc', 'google/protobuf/dynamic_message.cc', 'google/protobuf/duration.pb.cc', 'google/protobuf/descriptor_database.cc', 'google/protobuf/descriptor.pb.cc', 'google/protobuf/descriptor.cc', 'google/protobuf/compiler/parser.cc', 'google/protobuf/compiler/importer.cc', 'google/protobuf/api.pb.cc', 'google/protobuf/any.pb.cc', 'google/protobuf/any.cc', 'google/protobuf/wire_format_lite.cc', 'google/protobuf/stubs/time.cc', 'google/protobuf/stubs/strutil.cc', 'google/protobuf/stubs/structurally_valid.cc', 'google/protobuf/stubs/stringprintf.cc', 'google/protobuf/stubs/stringpiece.cc', 'google/protobuf/stubs/statusor.cc', 'google/protobuf/stubs/status.cc', 'google/protobuf/stubs/once.cc', 'google/protobuf/stubs/int128.cc', 'google/protobuf/stubs/common.cc', 'google/protobuf/stubs/bytestream.cc', 'google/protobuf/stubs/atomicops_internals_x86_msvc.cc', 'google/protobuf/stubs/atomicops_internals_x86_gcc.cc', 'google/protobuf/repeated_field.cc', 'google/protobuf/message_lite.cc', 'google/protobuf/io/zero_copy_stream_impl_lite.cc', 'google/protobuf/io/zero_copy_stream.cc', 'google/protobuf/io/coded_stream.cc', 'google/protobuf/generated_message_util.cc', 'google/protobuf/extension_set.cc', 'google/protobuf/arenastring.cc', 'google/protobuf/arena.cc']
 PROTO_FILES=['google/protobuf/wrappers.proto', 'google/protobuf/type.proto', 'google/protobuf/timestamp.proto', 'google/protobuf/struct.proto', 'google/protobuf/source_context.proto', 'google/protobuf/field_mask.proto', 'google/protobuf/empty.proto', 'google/protobuf/duration.proto', 'google/protobuf/descriptor.proto', 'google/protobuf/compiler/plugin.proto', 'google/protobuf/api.proto', 'google/protobuf/any.proto']
 
 CC_INCLUDE='third_party/protobuf/src'
diff --git a/tools/distrib/python/grpcio_tools/setup.py b/tools/distrib/python/grpcio_tools/setup.py
index bb1f1cf..a07a586 100644
--- a/tools/distrib/python/grpcio_tools/setup.py
+++ b/tools/distrib/python/grpcio_tools/setup.py
@@ -27,6 +27,7 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from distutils import cygwinccompiler
 from distutils import extension
 from distutils import util
 import errno
@@ -68,26 +69,35 @@
 EXTRA_ENV_COMPILE_ARGS = os.environ.get('GRPC_PYTHON_CFLAGS', None)
 EXTRA_ENV_LINK_ARGS = os.environ.get('GRPC_PYTHON_LDFLAGS', None)
 if EXTRA_ENV_COMPILE_ARGS is None:
-  EXTRA_ENV_COMPILE_ARGS = '-fno-wrapv -frtti -std=c++11'
+  EXTRA_ENV_COMPILE_ARGS = '-std=c++11'
   if 'win32' in sys.platform:
-    # We use define flags here and don't directly add to DEFINE_MACROS below to
-    # ensure that the expert user/builder has a way of turning it off (via the
-    # envvars) without adding yet more GRPC-specific envvars.
-    # See https://sourceforge.net/p/mingw-w64/bugs/363/
-    if '32' in platform.architecture()[0]:
-      EXTRA_ENV_COMPILE_ARGS += ' -D_ftime=_ftime32 -D_timeb=__timeb32 -D_ftime_s=_ftime32_s'
+    if sys.version_info < (3, 5):
+      # We use define flags here and don't directly add to DEFINE_MACROS below to
+      # ensure that the expert user/builder has a way of turning it off (via the
+      # envvars) without adding yet more GRPC-specific envvars.
+      # See https://sourceforge.net/p/mingw-w64/bugs/363/
+      if '32' in platform.architecture()[0]:
+        EXTRA_ENV_COMPILE_ARGS += ' -D_ftime=_ftime32 -D_timeb=__timeb32 -D_ftime_s=_ftime32_s'
+      else:
+        EXTRA_ENV_COMPILE_ARGS += ' -D_ftime=_ftime64 -D_timeb=__timeb64'
     else:
-      EXTRA_ENV_COMPILE_ARGS += ' -D_ftime=_ftime64 -D_timeb=__timeb64'
+      # We need to statically link the C++ Runtime, only the C runtime is
+      # available dynamically
+      EXTRA_ENV_COMPILE_ARGS += ' /MT'
+  elif "linux" in sys.platform or "darwin" in sys.platform:
+    EXTRA_ENV_COMPILE_ARGS += ' -fno-wrapv -frtti'
 if EXTRA_ENV_LINK_ARGS is None:
-  EXTRA_ENV_LINK_ARGS = '-lpthread'
-  if 'win32' in sys.platform:
-    # TODO(atash) check if this is actually safe to just import and call on
-    # non-Windows (to avoid breaking import style)
-    from distutils.cygwinccompiler import get_msvcr
-    msvcr = get_msvcr()[0]
+  EXTRA_ENV_LINK_ARGS = ''
+  if "linux" in sys.platform or "darwin" in sys.platform:
+    EXTRA_ENV_LINK_ARGS += ' -lpthread'
+  elif "win32" in sys.platform and sys.version_info < (3, 5):
+    msvcr = cygwinccompiler.get_msvcr()[0]
+    # TODO(atash) sift through the GCC specs to see if libstdc++ can have any
+    # influence on the linkage outcome on MinGW for non-C++ programs.
     EXTRA_ENV_LINK_ARGS += (
         ' -static-libgcc -static-libstdc++ -mcrtdll={msvcr} '
         '-static'.format(msvcr=msvcr))
+
 EXTRA_COMPILE_ARGS = shlex.split(EXTRA_ENV_COMPILE_ARGS)
 EXTRA_LINK_ARGS = shlex.split(EXTRA_ENV_LINK_ARGS)
 
@@ -101,9 +111,13 @@
 GRPC_PYTHON_TOOLS_PACKAGE = 'grpc.tools'
 GRPC_PYTHON_PROTO_RESOURCES_NAME = '_proto'
 
-DEFINE_MACROS = (('HAVE_PTHREAD', 1),)
-if "win32" in sys.platform and '64bit' in platform.architecture()[0]:
-  DEFINE_MACROS += (('MS_WIN64', 1),)
+DEFINE_MACROS = ()
+if "win32" in sys.platform:
+  DEFINE_MACROS += (('WIN32_LEAN_AND_MEAN', 1),)
+  if '64bit' in platform.architecture()[0]:
+    DEFINE_MACROS += (('MS_WIN64', 1),)
+elif "linux" in sys.platform or "darwin" in sys.platform:
+  DEFINE_MACROS += (('HAVE_PTHREAD', 1),)
 
 # By default, Python3 distutils enforces compatibility of
 # c plugins (.so files) with the OSX version Python3 was built with.
@@ -177,8 +191,8 @@
   packages=setuptools.find_packages('.'),
   namespace_packages=['grpc'],
   install_requires=[
-    'protobuf>=3.0.0a3',
-    'grpcio>=0.15.0',
+    'protobuf>=3.0.0',
+    'grpcio>={version}'.format(version=grpc_version.VERSION),
   ],
   package_data=package_data(),
 )
diff --git a/tools/distrib/python/make_grpcio_tools.py b/tools/distrib/python/make_grpcio_tools.py
index adf5844..7413928 100755
--- a/tools/distrib/python/make_grpcio_tools.py
+++ b/tools/distrib/python/make_grpcio_tools.py
@@ -88,22 +88,23 @@
     os.path.join(os.path.dirname(os.path.abspath(__file__)),
                  '..', '..', '..'))
 
-GRPC_PYTHON_ROOT = os.path.join(GRPC_ROOT, 'tools/distrib/python/grpcio_tools')
+GRPC_PYTHON_ROOT = os.path.join(GRPC_ROOT, 'tools', 'distrib',
+                                'python', 'grpcio_tools')
 
-GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT = 'third_party/protobuf/src'
+GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT = os.path.join('third_party', 'protobuf', 'src')
 GRPC_PROTOBUF = os.path.join(GRPC_ROOT, GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT)
-GRPC_PROTOC_PLUGINS = os.path.join(GRPC_ROOT, 'src/compiler')
-GRPC_PYTHON_PROTOBUF = os.path.join(GRPC_PYTHON_ROOT,
-                                    'third_party/protobuf/src')
-GRPC_PYTHON_PROTOC_PLUGINS = os.path.join(GRPC_PYTHON_ROOT,
-                                          'grpc_root/src/compiler')
+GRPC_PROTOC_PLUGINS = os.path.join(GRPC_ROOT, 'src', 'compiler')
+GRPC_PYTHON_PROTOBUF = os.path.join(GRPC_PYTHON_ROOT, 'third_party', 'protobuf',
+                                    'src')
+GRPC_PYTHON_PROTOC_PLUGINS = os.path.join(GRPC_PYTHON_ROOT, 'grpc_root', 'src',
+                                          'compiler')
 GRPC_PYTHON_PROTOC_LIB_DEPS = os.path.join(GRPC_PYTHON_ROOT,
                                            'protoc_lib_deps.py')
 
 GRPC_INCLUDE = os.path.join(GRPC_ROOT, 'include')
-GRPC_PYTHON_INCLUDE = os.path.join(GRPC_PYTHON_ROOT, 'grpc_root/include')
+GRPC_PYTHON_INCLUDE = os.path.join(GRPC_PYTHON_ROOT, 'grpc_root', 'include')
 
-BAZEL_DEPS = os.path.join(GRPC_ROOT, 'tools/distrib/python/bazel_deps.sh')
+BAZEL_DEPS = os.path.join(GRPC_ROOT, 'tools', 'distrib', 'python', 'bazel_deps.sh')
 BAZEL_DEPS_PROTOC_LIB_QUERY = '//:protoc_lib'
 BAZEL_DEPS_COMMON_PROTOS_QUERY = '//:well_known_protos'
 
@@ -136,64 +137,6 @@
   else:
     return path
 
-def atomic_file_copy(src, dst):
-  """Based on the lock-free-whack-a-mole algorithm, depending on filesystem
-     renaming being atomic. Described at http://stackoverflow.com/a/28090883.
-  """
-  try:
-    if filecmp.cmp(src, dst):
-      return
-  except:
-    pass
-  dst_dir = os.path.abspath(os.path.dirname(dst))
-  dst_base = os.path.basename(dst)
-  this_id = str(uuid.uuid4()).replace('.', '-')
-  temporary_file = os.path.join(dst_dir, '{}.{}.tmp'.format(dst_base, this_id))
-  mole_file = os.path.join(dst_dir, '{}.{}.mole.tmp'.format(dst_base, this_id))
-  mole_pattern = os.path.join(dst_dir, '{}.*.mole.tmp'.format(dst_base))
-  src = long_path(src)
-  dst = long_path(dst)
-  temporary_file = long_path(temporary_file)
-  mole_file = long_path(mole_file)
-  mole_pattern = long_path(mole_pattern)
-  shutil.copy2(src, temporary_file)
-  try:
-    os.rename(temporary_file, mole_file)
-  except:
-    print('Error moving temporary file {} to {}'.format(temporary_file, mole_file), file=sys.stderr)
-    print('while trying to copy file {} to {}'.format(src, dst), file=sys.stderr)
-    raise
-  for other_file in glob.glob(mole_pattern):
-    other_id = other_file.split('.')[-3]
-    if this_id == other_id:
-      pass
-    elif this_id < other_id:
-      try:
-        os.remove(other_file)
-      except:
-        pass
-    else:
-      try:
-        os.remove(mole_file)
-      except:
-        pass
-      this_id = other_id
-      mole_file = other_file
-  try:
-    if filecmp.cmp(src, dst):
-      try:
-        os.remove(mole_file)
-      except:
-        pass
-      return
-  except:
-    pass
-  try:
-    os.rename(mole_file, dst)
-  except:
-    pass
-
-
 def main():
   os.chdir(GRPC_ROOT)
 
@@ -211,7 +154,7 @@
       for relative_file in files:
         source_file = os.path.abspath(os.path.join(source_dir, relative_file))
         target_file = os.path.abspath(os.path.join(target_dir, relative_file))
-        atomic_file_copy(source_file, target_file)
+        shutil.copyfile(source_file, target_file)
 
   try:
     protoc_lib_deps_content = get_deps()
diff --git a/tools/dockerfile/distribtest/csharp_centos7_x64/Dockerfile b/tools/dockerfile/distribtest/csharp_centos7_x64/Dockerfile
index c917b29..c8b5daa 100644
--- a/tools/dockerfile/distribtest/csharp_centos7_x64/Dockerfile
+++ b/tools/dockerfile/distribtest/csharp_centos7_x64/Dockerfile
@@ -33,5 +33,17 @@
 RUN yum-config-manager --add-repo http://download.mono-project.com/repo/centos/
 
 RUN yum install -y mono
+
 RUN yum install -y unzip
-RUN yum install -y nuget
+
+# --nogpgcheck because nuget-2.12 package is not signed.
+RUN yum install -y nuget --nogpgcheck
+
+# Help mono correctly locate libMonoPosixHelper.so
+# as a workaround for issue https://bugzilla.xamarin.com/show_bug.cgi?id=42820
+# The error message you'll get without this workaround:
+# ```
+# WARNING: /usr/lib/libMonoPosixHelper.so
+# WARNING: Unable to read package from path 'Grpc.1.1.0-dev.nupkg'.
+# ```
+RUN cp /usr/lib64/libMonoPosixHelper.so /usr/lib/
diff --git a/tools/dockerfile/distribtest/csharp_jessie_x64/Dockerfile b/tools/dockerfile/distribtest/csharp_jessie_x64/Dockerfile
index 249095a..c8cd575 100644
--- a/tools/dockerfile/distribtest/csharp_jessie_x64/Dockerfile
+++ b/tools/dockerfile/distribtest/csharp_jessie_x64/Dockerfile
@@ -40,4 +40,7 @@
     ca-certificates-mono \
     nuget
 
+# make sure we have nuget 2.12+ (in case there's an older cached docker image)
+RUN apt-get update && apt-get install -y nuget
+
 RUN apt-get update && apt-get install -y unzip
diff --git a/tools/dockerfile/distribtest/csharp_jessie_x86/Dockerfile b/tools/dockerfile/distribtest/csharp_jessie_x86/Dockerfile
index 9eda551..28cc654 100644
--- a/tools/dockerfile/distribtest/csharp_jessie_x86/Dockerfile
+++ b/tools/dockerfile/distribtest/csharp_jessie_x86/Dockerfile
@@ -40,4 +40,7 @@
     ca-certificates-mono \
     nuget
 
+# make sure we have nuget 2.12+ (in case there's an older cached docker image)
+RUN apt-get update && apt-get install -y nuget
+
 RUN apt-get update && apt-get install -y unzip
diff --git a/tools/dockerfile/distribtest/csharp_ubuntu1404_x64/Dockerfile b/tools/dockerfile/distribtest/csharp_ubuntu1404_x64/Dockerfile
index 85daea4..ed6d87b 100644
--- a/tools/dockerfile/distribtest/csharp_ubuntu1404_x64/Dockerfile
+++ b/tools/dockerfile/distribtest/csharp_ubuntu1404_x64/Dockerfile
@@ -37,4 +37,20 @@
     ca-certificates-mono \
     nuget
 
+# make sure we have nuget 2.12+ (in case there's an older cached docker image)
+RUN apt-get update && apt-get install -y nuget
+
 RUN apt-get update && apt-get install -y unzip
+
+# Install dotnet CLI
+RUN apt-get install -y apt-transport-https
+RUN sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ trusty main" > /etc/apt/sources.list.d/dotnetdev.list'
+RUN apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893
+RUN apt-get update && apt-get install -y dotnet-dev-1.0.0-preview2-003121
+
+# Trigger the population of the local package cache for dotnet CLI
+RUN mkdir warmup \
+    && cd warmup \
+    && dotnet new \
+    && cd .. \
+    && rm -rf warmup
diff --git a/tools/dockerfile/distribtest/csharp_ubuntu1504_x64/Dockerfile b/tools/dockerfile/distribtest/csharp_ubuntu1504_x64/Dockerfile
index ec16105..fd39ab2 100644
--- a/tools/dockerfile/distribtest/csharp_ubuntu1504_x64/Dockerfile
+++ b/tools/dockerfile/distribtest/csharp_ubuntu1504_x64/Dockerfile
@@ -37,4 +37,7 @@
     ca-certificates-mono \
     nuget
 
+# make sure we have nuget 2.12+ (in case there's an older cached docker image)
+RUN apt-get update && apt-get install -y nuget
+
 RUN apt-get update && apt-get install -y unzip
diff --git a/tools/dockerfile/distribtest/csharp_ubuntu1510_x64/Dockerfile b/tools/dockerfile/distribtest/csharp_ubuntu1510_x64/Dockerfile
index 110aa2a..1d86dbd 100644
--- a/tools/dockerfile/distribtest/csharp_ubuntu1510_x64/Dockerfile
+++ b/tools/dockerfile/distribtest/csharp_ubuntu1510_x64/Dockerfile
@@ -37,4 +37,7 @@
     ca-certificates-mono \
     nuget
 
+# make sure we have nuget 2.12+ (in case there's an older cached docker image)
+RUN apt-get update && apt-get install -y nuget
+
 RUN apt-get update && apt-get install -y unzip
diff --git a/tools/dockerfile/distribtest/csharp_ubuntu1604_x64/Dockerfile b/tools/dockerfile/distribtest/csharp_ubuntu1604_x64/Dockerfile
index 4ef0766..05fa32b 100644
--- a/tools/dockerfile/distribtest/csharp_ubuntu1604_x64/Dockerfile
+++ b/tools/dockerfile/distribtest/csharp_ubuntu1604_x64/Dockerfile
@@ -34,4 +34,9 @@
     ca-certificates-mono \
     nuget
 
+# make sure we have nuget 2.12+ (in case there's an older cached docker image)
+RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy main" | tee /etc/apt/sources.list.d/mono-xamarin.list
+RUN apt-get update && apt-get install -y nuget
+
 RUN apt-get update && apt-get install -y unzip
diff --git a/tools/dockerfile/distribtest/csharp_wheezy_x64/Dockerfile b/tools/dockerfile/distribtest/csharp_wheezy_x64/Dockerfile
index d9e64fe..0ab2a62 100644
--- a/tools/dockerfile/distribtest/csharp_wheezy_x64/Dockerfile
+++ b/tools/dockerfile/distribtest/csharp_wheezy_x64/Dockerfile
@@ -27,6 +27,9 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-FROM mono:4.2.2.30
+FROM mono:4.4.2.11
+
+# make sure we have nuget 2.12+ (in case there's an older cached docker image)
+RUN apt-get update && apt-get install -y nuget
 
 RUN apt-get update && apt-get install -y unzip
diff --git a/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile
index e3d52f0..087cc4e 100644
--- a/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile
@@ -95,6 +95,8 @@
     nuget \
     && apt-get clean
 
+RUN nuget update -self
+
 # Prepare ccache
 RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
 RUN ln -s /usr/bin/ccache /usr/local/bin/g++
diff --git a/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile
new file mode 100644
index 0000000..efe6e39
--- /dev/null
+++ b/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile
@@ -0,0 +1,126 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+FROM debian:jessie
+
+# Install Git and basic packages.
+RUN apt-get update && apt-get install -y \
+  autoconf \
+  autotools-dev \
+  build-essential \
+  bzip2 \
+  ccache \
+  curl \
+  gcc \
+  gcc-multilib \
+  git \
+  golang \
+  gyp \
+  lcov \
+  libc6 \
+  libc6-dbg \
+  libc6-dev \
+  libgtest-dev \
+  libtool \
+  make \
+  perl \
+  strace \
+  python-dev \
+  python-setuptools \
+  python-yaml \
+  telnet \
+  unzip \
+  wget \
+  zip && apt-get clean
+
+#================
+# Build profiling
+RUN apt-get update && apt-get install -y time && apt-get clean
+
+#====================
+# Python dependencies
+
+# Install dependencies
+
+RUN apt-get update && apt-get install -y \
+    python-all-dev \
+    python3-all-dev \
+    python-pip
+
+# Install Python packages from PyPI
+RUN pip install pip --upgrade
+RUN pip install virtualenv
+RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.0.0a2 six==1.10.0
+
+#================
+# C# dependencies
+
+# Update to a newer version of mono
+RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy main" | tee /etc/apt/sources.list.d/mono-xamarin.list
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy-apache24-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy-libjpeg62-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list
+RUN echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list
+
+# Install dependencies
+RUN apt-get update && apt-get -y dist-upgrade && apt-get install -y \
+    mono-devel \
+    ca-certificates-mono \
+    nuget \
+    && apt-get clean
+
+RUN nuget update -self
+
+# Install dotnet SDK based on https://www.microsoft.com/net/core#debian
+RUN apt-get update && apt-get install -y curl libunwind8 gettext
+RUN curl -sSL -o dotnet.tar.gz https://go.microsoft.com/fwlink/?LinkID=809130
+RUN mkdir -p /opt/dotnet && tar zxf dotnet.tar.gz -C /opt/dotnet
+RUN ln -s /opt/dotnet/dotnet /usr/local/bin
+
+# Trigger the population of the local package cache
+ENV NUGET_XMLDOC_MODE skip
+RUN mkdir warmup \
+    && cd warmup \
+    && dotnet new \
+    && cd .. \
+    && rm -rf warmup
+
+# Prepare ccache
+RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
+RUN ln -s /usr/bin/ccache /usr/local/bin/g++
+RUN ln -s /usr/bin/ccache /usr/local/bin/cc
+RUN ln -s /usr/bin/ccache /usr/local/bin/c++
+RUN ln -s /usr/bin/ccache /usr/local/bin/clang
+RUN ln -s /usr/bin/ccache /usr/local/bin/clang++
+
+
+RUN mkdir /var/local/jenkins
+
+# Define the default command.
+CMD ["bash"]
diff --git a/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/build_interop.sh b/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/build_interop.sh
new file mode 100755
index 0000000..77cd65c
--- /dev/null
+++ b/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/build_interop.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Builds C# interop server and client in a base image.
+set -e
+
+mkdir -p /var/local/git
+git clone --recursive /var/local/jenkins/grpc /var/local/git/grpc
+
+# copy service account keys if available
+cp -r /var/local/jenkins/service_account $HOME || true
+
+cd /var/local/git/grpc
+
+# build C# interop client & server
+tools/run_tests/run_tests.py -l csharp -c dbg --compiler coreclr --build_only
diff --git a/tools/dockerfile/interoptest/grpc_interop_go/build_interop.sh b/tools/dockerfile/interoptest/grpc_interop_go/build_interop.sh
index e891549..858ee0a 100755
--- a/tools/dockerfile/interoptest/grpc_interop_go/build_interop.sh
+++ b/tools/dockerfile/interoptest/grpc_interop_go/build_interop.sh
@@ -36,18 +36,12 @@
 # to test instead of using "go get" to download from Github directly.
 git clone --recursive /var/local/jenkins/grpc-go src/google.golang.org/grpc
 
+# Get all gRPC Go dependencies
+(cd src/google.golang.org/grpc && go get -t .)
+
 # copy service account keys if available
 cp -r /var/local/jenkins/service_account $HOME || true
 
-# Get dependencies from GitHub
-# NOTE: once grpc-go dependencies change, this needs to be updated manually
-# but we don't expect this to happen any time soon.
-go get github.com/golang/protobuf/proto
-go get golang.org/x/net/context
-go get golang.org/x/net/trace
-go get golang.org/x/oauth2
-go get google.golang.org/cloud
-
 # Build the interop client and server
 (cd src/google.golang.org/grpc/interop/client && go install)
 (cd src/google.golang.org/grpc/interop/server && go install)
diff --git a/tools/dockerfile/interoptest/grpc_interop_php/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_php/Dockerfile
index 0d6171c..d0e1a96 100644
--- a/tools/dockerfile/interoptest/grpc_interop_php/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_php/Dockerfile
@@ -63,21 +63,6 @@
 # Build profiling
 RUN apt-get update && apt-get install -y time && apt-get clean
 
-#==================
-# Ruby dependencies
-
-# Install rvm
-RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
-RUN \curl -sSL https://get.rvm.io | bash -s stable
-
-# Install Ruby 2.1
-RUN /bin/bash -l -c "rvm install ruby-2.1"
-RUN /bin/bash -l -c "rvm use --default ruby-2.1"
-RUN /bin/bash -l -c "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
-RUN /bin/bash -l -c "echo 'export PATH=/usr/local/rvm/bin:$PATH' >> ~/.bashrc"
-RUN /bin/bash -l -c "echo 'rvm --default use ruby-2.1' >> ~/.bashrc"
-RUN /bin/bash -l -c "gem install bundler --no-ri --no-rdoc"
-
 #=================
 # PHP dependencies
 
@@ -97,25 +82,10 @@
 
 RUN mkdir /var/local/jenkins
 
-# ronn: a ruby tool used to convert markdown to man pages, used during the
-# install of Protobuf extensions
-#
-# rake: a ruby version of make used to build the PHP Protobuf extension
-RUN /bin/bash -l -c "rvm all do gem install ronn rake"
-
 # Install composer
 RUN curl -sS https://getcomposer.org/installer | php
 RUN mv composer.phar /usr/local/bin/composer
 
-# Download the patched PHP protobuf so that PHP gRPC clients can be generated
-# from proto3 schemas.
-RUN git clone https://github.com/stanley-cheung/Protobuf-PHP.git /var/local/git/protobuf-php
-
-RUN /bin/bash -l -c "rvm use ruby-2.1 \
-  && cd /var/local/git/protobuf-php \
-  && rvm all do rake pear:package version=1.0 \
-  && pear install Protobuf-1.0.tgz"
-
 # Define the default command.
 CMD ["bash"]
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_php/build_interop.sh b/tools/dockerfile/interoptest/grpc_interop_php/build_interop.sh
index a84a450..cf5e888 100755
--- a/tools/dockerfile/interoptest/grpc_interop_php/build_interop.sh
+++ b/tools/dockerfile/interoptest/grpc_interop_php/build_interop.sh
@@ -38,7 +38,6 @@
 cp -r /var/local/jenkins/service_account $HOME || true
 
 cd /var/local/git/grpc
-rvm --default use ruby-2.1
 
 # gRPC core and protobuf need to be installed
 make install
@@ -49,4 +48,4 @@
 
 (cd src/php && composer install)
 
-(cd src/php && protoc-gen-php -i tests/interop/ -o tests/interop/ tests/interop/test.proto)
+(cd src/php && ./bin/generate_proto_php.sh)
diff --git a/tools/dockerfile/interoptest/grpc_interop_php7/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_php7/Dockerfile
index be8f25f..d8cbe70 100644
--- a/tools/dockerfile/interoptest/grpc_interop_php7/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_php7/Dockerfile
@@ -75,21 +75,6 @@
   && make \
   && make install
 
-#==================
-# Ruby dependencies
-
-# Install rvm
-RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
-RUN \curl -sSL https://get.rvm.io | bash -s stable
-
-# Install Ruby 2.1
-RUN /bin/bash -l -c "rvm install ruby-2.1"
-RUN /bin/bash -l -c "rvm use --default ruby-2.1"
-RUN /bin/bash -l -c "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
-RUN /bin/bash -l -c "echo 'export PATH=/usr/local/rvm/bin:$PATH' >> ~/.bashrc"
-RUN /bin/bash -l -c "echo 'rvm --default use ruby-2.1' >> ~/.bashrc"
-RUN /bin/bash -l -c "gem install bundler --no-ri --no-rdoc"
-
 # Prepare ccache
 RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
 RUN ln -s /usr/bin/ccache /usr/local/bin/g++
@@ -101,25 +86,10 @@
 
 RUN mkdir /var/local/jenkins
 
-# ronn: a ruby tool used to convert markdown to man pages, used during the
-# install of Protobuf extensions
-#
-# rake: a ruby version of make used to build the PHP Protobuf extension
-RUN /bin/bash -l -c "rvm all do gem install ronn rake"
-
 # Install composer
 RUN curl -sS https://getcomposer.org/installer | php
 RUN mv composer.phar /usr/local/bin/composer
 
-# Download the patched PHP protobuf so that PHP gRPC clients can be generated
-# from proto3 schemas.
-RUN git clone https://github.com/stanley-cheung/Protobuf-PHP.git /var/local/git/protobuf-php
-
-RUN /bin/bash -l -c "rvm use ruby-2.1 \
-  && cd /var/local/git/protobuf-php \
-  && rvm all do rake pear:package version=1.0 \
-  && pear install Protobuf-1.0.tgz"
-
 # Define the default command.
 CMD ["bash"]
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_php7/build_interop.sh b/tools/dockerfile/interoptest/grpc_interop_php7/build_interop.sh
index 261dded..e486e52 100755
--- a/tools/dockerfile/interoptest/grpc_interop_php7/build_interop.sh
+++ b/tools/dockerfile/interoptest/grpc_interop_php7/build_interop.sh
@@ -38,7 +38,6 @@
 cp -r /var/local/jenkins/service_account $HOME || true
 
 cd /var/local/git/grpc
-rvm --default use ruby-2.1
 
 # gRPC core and protobuf need to be installed
 make install
@@ -49,4 +48,4 @@
 
 (cd src/php && composer install)
 
-(cd src/php && protoc-gen-php -i tests/interop/ -o tests/interop/ tests/interop/test.proto)
+(cd src/php && ./bin/generate_proto_php.sh)
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_csharp/Dockerfile b/tools/dockerfile/stress_test/grpc_interop_stress_csharp/Dockerfile
index 81e3fdc..3288253 100644
--- a/tools/dockerfile/stress_test/grpc_interop_stress_csharp/Dockerfile
+++ b/tools/dockerfile/stress_test/grpc_interop_stress_csharp/Dockerfile
@@ -112,5 +112,7 @@
     nuget \
     && apt-get clean
 
+RUN nuget update -self
+
 # Define the default command.
 CMD ["bash"]
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile b/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile
index 0716be5..0fe9c15 100644
--- a/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile
+++ b/tools/dockerfile/stress_test/grpc_interop_stress_php/Dockerfile
@@ -117,24 +117,9 @@
 
 RUN mkdir /var/local/jenkins
 
-# ronn: a ruby tool used to convert markdown to man pages, used during the
-# install of Protobuf extensions
-#
-# rake: a ruby version of make used to build the PHP Protobuf extension
-RUN /bin/bash -l -c "rvm all do gem install ronn rake"
-
 # Install composer
 RUN curl -sS https://getcomposer.org/installer | php
 RUN mv composer.phar /usr/local/bin/composer
 
-# Download the patched PHP protobuf so that PHP gRPC clients can be generated
-# from proto3 schemas.
-RUN git clone https://github.com/stanley-cheung/Protobuf-PHP.git /var/local/git/protobuf-php
-
-RUN /bin/bash -l -c "rvm use ruby-2.1 \
-  && cd /var/local/git/protobuf-php \
-  && rvm all do rake pear:package version=1.0 \
-  && pear install Protobuf-1.0.tgz"
-
 # Define the default command.
 CMD ["bash"]
diff --git a/tools/dockerfile/stress_test/grpc_interop_stress_php/build_interop_stress.sh b/tools/dockerfile/stress_test/grpc_interop_stress_php/build_interop_stress.sh
index 87262f1..34fd09f 100755
--- a/tools/dockerfile/stress_test/grpc_interop_stress_php/build_interop_stress.sh
+++ b/tools/dockerfile/stress_test/grpc_interop_stress_php/build_interop_stress.sh
@@ -38,7 +38,6 @@
 cp -r /var/local/jenkins/service_account $HOME || true
 
 cd /var/local/git/grpc
-rvm --default use ruby-2.1
 
 make install-certs
 
@@ -51,4 +50,4 @@
 
 (cd src/php && composer install)
 
-(cd src/php && protoc-gen-php -i tests/interop/ -o tests/interop/ tests/interop/test.proto)
+(cd src/php && ./bin/generate_proto_php.sh)
diff --git a/tools/dockerfile/test/csharp_coreclr_x64/Dockerfile b/tools/dockerfile/test/csharp_coreclr_x64/Dockerfile
index 25c6fe6..efe6e39 100644
--- a/tools/dockerfile/test/csharp_coreclr_x64/Dockerfile
+++ b/tools/dockerfile/test/csharp_coreclr_x64/Dockerfile
@@ -95,6 +95,7 @@
     nuget \
     && apt-get clean
 
+RUN nuget update -self
 
 # Install dotnet SDK based on https://www.microsoft.com/net/core#debian
 RUN apt-get update && apt-get install -y curl libunwind8 gettext
diff --git a/tools/dockerfile/test/csharp_jessie_x64/Dockerfile b/tools/dockerfile/test/csharp_jessie_x64/Dockerfile
index e3d52f0..087cc4e 100644
--- a/tools/dockerfile/test/csharp_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/csharp_jessie_x64/Dockerfile
@@ -95,6 +95,8 @@
     nuget \
     && apt-get clean
 
+RUN nuget update -self
+
 # Prepare ccache
 RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
 RUN ln -s /usr/bin/ccache /usr/local/bin/g++
diff --git a/tools/dockerfile/test/multilang_jessie_x64/Dockerfile b/tools/dockerfile/test/multilang_jessie_x64/Dockerfile
index 13f7c10..2540b52 100644
--- a/tools/dockerfile/test/multilang_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/multilang_jessie_x64/Dockerfile
@@ -80,6 +80,8 @@
     nuget \
     && apt-get clean
 
+RUN nuget update -self
+
 #=================
 # C++ dependencies
 RUN apt-get update && apt-get -y install libgflags-dev libgtest-dev libc++-dev clang && apt-get clean
diff --git a/tools/dockerfile/test/python_pyenv_x64/Dockerfile b/tools/dockerfile/test/python_pyenv_x64/Dockerfile
index abb5f3c..ecd785a 100644
--- a/tools/dockerfile/test/python_pyenv_x64/Dockerfile
+++ b/tools/dockerfile/test/python_pyenv_x64/Dockerfile
@@ -95,7 +95,8 @@
 RUN pyenv update
 RUN pyenv install 3.5-dev
 RUN pyenv install 3.6-dev
-RUN pyenv local 3.5-dev 3.6-dev
+RUN pyenv install pypy-5.3.1
+RUN pyenv local 3.5-dev 3.6-dev pypy-5.3.1
 
 # Prepare ccache
 RUN ln -s /usr/bin/ccache /usr/local/bin/gcc
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index a2415e1..4b0be37 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -830,6 +830,7 @@
 include/grpc++/impl/codegen/service_type.h \
 include/grpc++/impl/codegen/status.h \
 include/grpc++/impl/codegen/status_code_enum.h \
+include/grpc++/impl/codegen/status_helper.h \
 include/grpc++/impl/codegen/string_ref.h \
 include/grpc++/impl/codegen/stub_options.h \
 include/grpc++/impl/codegen/sync.h \
@@ -837,27 +838,23 @@
 include/grpc++/impl/codegen/sync_no_cxx11.h \
 include/grpc++/impl/codegen/sync_stream.h \
 include/grpc++/impl/codegen/time.h \
-include/grpc/impl/codegen/byte_buffer.h \
 include/grpc/impl/codegen/byte_buffer_reader.h \
 include/grpc/impl/codegen/compression_types.h \
 include/grpc/impl/codegen/connectivity_state.h \
 include/grpc/impl/codegen/grpc_types.h \
 include/grpc/impl/codegen/propagation_bits.h \
 include/grpc/impl/codegen/status.h \
-include/grpc/impl/codegen/alloc.h \
 include/grpc/impl/codegen/atm.h \
 include/grpc/impl/codegen/atm_gcc_atomic.h \
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
-include/grpc/impl/codegen/log.h \
+include/grpc/impl/codegen/gpr_types.h \
 include/grpc/impl/codegen/port_platform.h \
 include/grpc/impl/codegen/slice.h \
-include/grpc/impl/codegen/slice_buffer.h \
 include/grpc/impl/codegen/sync.h \
 include/grpc/impl/codegen/sync_generic.h \
 include/grpc/impl/codegen/sync_posix.h \
-include/grpc/impl/codegen/sync_windows.h \
-include/grpc/impl/codegen/time.h
+include/grpc/impl/codegen/sync_windows.h
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 945298b..e266e15 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -830,6 +830,7 @@
 include/grpc++/impl/codegen/service_type.h \
 include/grpc++/impl/codegen/status.h \
 include/grpc++/impl/codegen/status_code_enum.h \
+include/grpc++/impl/codegen/status_helper.h \
 include/grpc++/impl/codegen/string_ref.h \
 include/grpc++/impl/codegen/stub_options.h \
 include/grpc++/impl/codegen/sync.h \
@@ -837,66 +838,64 @@
 include/grpc++/impl/codegen/sync_no_cxx11.h \
 include/grpc++/impl/codegen/sync_stream.h \
 include/grpc++/impl/codegen/time.h \
-include/grpc/impl/codegen/byte_buffer.h \
 include/grpc/impl/codegen/byte_buffer_reader.h \
 include/grpc/impl/codegen/compression_types.h \
 include/grpc/impl/codegen/connectivity_state.h \
 include/grpc/impl/codegen/grpc_types.h \
 include/grpc/impl/codegen/propagation_bits.h \
 include/grpc/impl/codegen/status.h \
-include/grpc/impl/codegen/alloc.h \
 include/grpc/impl/codegen/atm.h \
 include/grpc/impl/codegen/atm_gcc_atomic.h \
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
-include/grpc/impl/codegen/log.h \
+include/grpc/impl/codegen/gpr_types.h \
 include/grpc/impl/codegen/port_platform.h \
 include/grpc/impl/codegen/slice.h \
-include/grpc/impl/codegen/slice_buffer.h \
 include/grpc/impl/codegen/sync.h \
 include/grpc/impl/codegen/sync_generic.h \
 include/grpc/impl/codegen/sync_posix.h \
 include/grpc/impl/codegen/sync_windows.h \
-include/grpc/impl/codegen/time.h \
 include/grpc++/impl/codegen/core_codegen.h \
 src/cpp/client/secure_credentials.h \
 src/cpp/common/secure_auth_context.h \
 src/cpp/server/secure_server_credentials.h \
 src/cpp/client/create_channel_internal.h \
+src/cpp/common/channel_filter.h \
 src/cpp/server/dynamic_thread_pool.h \
 src/cpp/server/thread_pool_interface.h \
+src/cpp/client/insecure_credentials.cc \
 src/cpp/client/secure_credentials.cc \
 src/cpp/common/auth_property_iterator.cc \
 src/cpp/common/secure_auth_context.cc \
 src/cpp/common/secure_channel_arguments.cc \
 src/cpp/common/secure_create_auth_context.cc \
+src/cpp/server/insecure_server_credentials.cc \
 src/cpp/server/secure_server_credentials.cc \
-src/cpp/client/channel.cc \
+src/cpp/client/channel_cc.cc \
 src/cpp/client/client_context.cc \
 src/cpp/client/create_channel.cc \
 src/cpp/client/create_channel_internal.cc \
 src/cpp/client/create_channel_posix.cc \
-src/cpp/client/credentials.cc \
+src/cpp/client/credentials_cc.cc \
 src/cpp/client/generic_stub.cc \
-src/cpp/client/insecure_credentials.cc \
 src/cpp/common/channel_arguments.cc \
-src/cpp/common/completion_queue.cc \
+src/cpp/common/channel_filter.cc \
+src/cpp/common/completion_queue_cc.cc \
 src/cpp/common/core_codegen.cc \
 src/cpp/common/rpc_method.cc \
 src/cpp/server/async_generic_service.cc \
 src/cpp/server/create_default_thread_pool.cc \
 src/cpp/server/dynamic_thread_pool.cc \
-src/cpp/server/insecure_server_credentials.cc \
-src/cpp/server/server.cc \
 src/cpp/server/server_builder.cc \
+src/cpp/server/server_cc.cc \
 src/cpp/server/server_context.cc \
 src/cpp/server/server_credentials.cc \
 src/cpp/server/server_posix.cc \
-src/cpp/util/byte_buffer.cc \
-src/cpp/util/slice.cc \
+src/cpp/util/byte_buffer_cc.cc \
+src/cpp/util/slice_cc.cc \
 src/cpp/util/status.cc \
 src/cpp/util/string_ref.cc \
-src/cpp/util/time.cc \
+src/cpp/util/time_cc.cc \
 src/cpp/codegen/codegen_init.cc
 
 # This tag can be used to specify the character encoding of the source files
diff --git a/tools/doxygen/Doxyfile.core b/tools/doxygen/Doxyfile.core
index e631c96..003686c 100644
--- a/tools/doxygen/Doxyfile.core
+++ b/tools/doxygen/Doxyfile.core
@@ -765,30 +765,26 @@
 include/grpc/compression.h \
 include/grpc/grpc.h \
 include/grpc/grpc_posix.h \
+include/grpc/grpc_security_constants.h \
 include/grpc/status.h \
-include/grpc/impl/codegen/byte_buffer.h \
 include/grpc/impl/codegen/byte_buffer_reader.h \
 include/grpc/impl/codegen/compression_types.h \
 include/grpc/impl/codegen/connectivity_state.h \
 include/grpc/impl/codegen/grpc_types.h \
 include/grpc/impl/codegen/propagation_bits.h \
 include/grpc/impl/codegen/status.h \
-include/grpc/impl/codegen/alloc.h \
 include/grpc/impl/codegen/atm.h \
 include/grpc/impl/codegen/atm_gcc_atomic.h \
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
-include/grpc/impl/codegen/log.h \
+include/grpc/impl/codegen/gpr_types.h \
 include/grpc/impl/codegen/port_platform.h \
 include/grpc/impl/codegen/slice.h \
-include/grpc/impl/codegen/slice_buffer.h \
 include/grpc/impl/codegen/sync.h \
 include/grpc/impl/codegen/sync_generic.h \
 include/grpc/impl/codegen/sync_posix.h \
 include/grpc/impl/codegen/sync_windows.h \
-include/grpc/impl/codegen/time.h \
 include/grpc/grpc_security.h \
-include/grpc/grpc_security_constants.h \
 include/grpc/census.h \
 include/grpc/support/alloc.h \
 include/grpc/support/atm.h \
@@ -818,20 +814,17 @@
 include/grpc/support/tls_msvc.h \
 include/grpc/support/tls_pthread.h \
 include/grpc/support/useful.h \
-include/grpc/impl/codegen/alloc.h \
 include/grpc/impl/codegen/atm.h \
 include/grpc/impl/codegen/atm_gcc_atomic.h \
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
-include/grpc/impl/codegen/log.h \
+include/grpc/impl/codegen/gpr_types.h \
 include/grpc/impl/codegen/port_platform.h \
 include/grpc/impl/codegen/slice.h \
-include/grpc/impl/codegen/slice_buffer.h \
 include/grpc/impl/codegen/sync.h \
 include/grpc/impl/codegen/sync_generic.h \
 include/grpc/impl/codegen/sync_posix.h \
-include/grpc/impl/codegen/sync_windows.h \
-include/grpc/impl/codegen/time.h
+include/grpc/impl/codegen/sync_windows.h
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 62cf5cc..44f147a 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -765,30 +765,26 @@
 include/grpc/compression.h \
 include/grpc/grpc.h \
 include/grpc/grpc_posix.h \
+include/grpc/grpc_security_constants.h \
 include/grpc/status.h \
-include/grpc/impl/codegen/byte_buffer.h \
 include/grpc/impl/codegen/byte_buffer_reader.h \
 include/grpc/impl/codegen/compression_types.h \
 include/grpc/impl/codegen/connectivity_state.h \
 include/grpc/impl/codegen/grpc_types.h \
 include/grpc/impl/codegen/propagation_bits.h \
 include/grpc/impl/codegen/status.h \
-include/grpc/impl/codegen/alloc.h \
 include/grpc/impl/codegen/atm.h \
 include/grpc/impl/codegen/atm_gcc_atomic.h \
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
-include/grpc/impl/codegen/log.h \
+include/grpc/impl/codegen/gpr_types.h \
 include/grpc/impl/codegen/port_platform.h \
 include/grpc/impl/codegen/slice.h \
-include/grpc/impl/codegen/slice_buffer.h \
 include/grpc/impl/codegen/sync.h \
 include/grpc/impl/codegen/sync_generic.h \
 include/grpc/impl/codegen/sync_posix.h \
 include/grpc/impl/codegen/sync_windows.h \
-include/grpc/impl/codegen/time.h \
 include/grpc/grpc_security.h \
-include/grpc/grpc_security_constants.h \
 include/grpc/census.h \
 src/core/lib/channel/channel_args.h \
 src/core/lib/channel/channel_stack.h \
@@ -796,9 +792,11 @@
 src/core/lib/channel/compress_filter.h \
 src/core/lib/channel/connected_channel.h \
 src/core/lib/channel/context.h \
+src/core/lib/channel/deadline_filter.h \
 src/core/lib/channel/handshaker.h \
 src/core/lib/channel/http_client_filter.h \
 src/core/lib/channel/http_server_filter.h \
+src/core/lib/channel/message_size_filter.h \
 src/core/lib/compression/algorithm_metadata.h \
 src/core/lib/compression/message_compress.h \
 src/core/lib/debug/trace.h \
@@ -806,6 +804,7 @@
 src/core/lib/http/httpcli.h \
 src/core/lib/http/parser.h \
 src/core/lib/iomgr/closure.h \
+src/core/lib/iomgr/combiner.h \
 src/core/lib/iomgr/endpoint.h \
 src/core/lib/iomgr/endpoint_pair.h \
 src/core/lib/iomgr/error.h \
@@ -867,6 +866,7 @@
 src/core/lib/transport/metadata.h \
 src/core/lib/transport/metadata_batch.h \
 src/core/lib/transport/static_metadata.h \
+src/core/lib/transport/timeout_encoding.h \
 src/core/lib/transport/transport.h \
 src/core/lib/transport/transport_impl.h \
 src/core/ext/transport/chttp2/transport/bin_decoder.h \
@@ -888,7 +888,6 @@
 src/core/ext/transport/chttp2/transport/internal.h \
 src/core/ext/transport/chttp2/transport/status_conversion.h \
 src/core/ext/transport/chttp2/transport/stream_map.h \
-src/core/ext/transport/chttp2/transport/timeout_encoding.h \
 src/core/ext/transport/chttp2/transport/varint.h \
 src/core/ext/transport/chttp2/alpn/alpn.h \
 src/core/lib/security/context/security_context.h \
@@ -917,8 +916,8 @@
 src/core/lib/tsi/transport_security_interface.h \
 src/core/ext/client_config/client_channel.h \
 src/core/ext/client_config/client_channel_factory.h \
-src/core/ext/client_config/client_config.h \
 src/core/ext/client_config/connector.h \
+src/core/ext/client_config/http_connect_handshaker.h \
 src/core/ext/client_config/initial_connect_string.h \
 src/core/ext/client_config/lb_policy.h \
 src/core/ext/client_config/lb_policy_factory.h \
@@ -927,10 +926,11 @@
 src/core/ext/client_config/resolver.h \
 src/core/ext/client_config/resolver_factory.h \
 src/core/ext/client_config/resolver_registry.h \
+src/core/ext/client_config/resolver_result.h \
 src/core/ext/client_config/subchannel.h \
-src/core/ext/client_config/subchannel_call_holder.h \
 src/core/ext/client_config/subchannel_index.h \
 src/core/ext/client_config/uri_parser.h \
+src/core/ext/lb_policy/grpclb/grpclb.h \
 src/core/ext/lb_policy/grpclb/load_balancer_api.h \
 src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h \
 third_party/nanopb/pb.h \
@@ -940,21 +940,27 @@
 src/core/ext/load_reporting/load_reporting.h \
 src/core/ext/load_reporting/load_reporting_filter.h \
 src/core/ext/census/aggregation.h \
+src/core/ext/census/base_resources.h \
 src/core/ext/census/census_interface.h \
 src/core/ext/census/census_rpc_stats.h \
 src/core/ext/census/gen/census.pb.h \
+src/core/ext/census/gen/trace_context.pb.h \
 src/core/ext/census/grpc_filter.h \
 src/core/ext/census/mlog.h \
+src/core/ext/census/resource.h \
 src/core/ext/census/rpc_metric_id.h \
+src/core/ext/census/trace_context.h \
 src/core/lib/surface/init.c \
 src/core/lib/channel/channel_args.c \
 src/core/lib/channel/channel_stack.c \
 src/core/lib/channel/channel_stack_builder.c \
 src/core/lib/channel/compress_filter.c \
 src/core/lib/channel/connected_channel.c \
+src/core/lib/channel/deadline_filter.c \
 src/core/lib/channel/handshaker.c \
 src/core/lib/channel/http_client_filter.c \
 src/core/lib/channel/http_server_filter.c \
+src/core/lib/channel/message_size_filter.c \
 src/core/lib/compression/compression.c \
 src/core/lib/compression/message_compress.c \
 src/core/lib/debug/trace.c \
@@ -962,6 +968,7 @@
 src/core/lib/http/httpcli.c \
 src/core/lib/http/parser.c \
 src/core/lib/iomgr/closure.c \
+src/core/lib/iomgr/combiner.c \
 src/core/lib/iomgr/endpoint.c \
 src/core/lib/iomgr/endpoint_pair_posix.c \
 src/core/lib/iomgr/endpoint_pair_windows.c \
@@ -1033,6 +1040,7 @@
 src/core/lib/transport/metadata.c \
 src/core/lib/transport/metadata_batch.c \
 src/core/lib/transport/static_metadata.c \
+src/core/lib/transport/timeout_encoding.c \
 src/core/lib/transport/transport.c \
 src/core/lib/transport/transport_op_string.c \
 src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c \
@@ -1055,7 +1063,6 @@
 src/core/ext/transport/chttp2/transport/status_conversion.c \
 src/core/ext/transport/chttp2/transport/stream_lists.c \
 src/core/ext/transport/chttp2/transport/stream_map.c \
-src/core/ext/transport/chttp2/transport/timeout_encoding.c \
 src/core/ext/transport/chttp2/transport/varint.c \
 src/core/ext/transport/chttp2/transport/writing.c \
 src/core/ext/transport/chttp2/alpn/alpn.c \
@@ -1091,10 +1098,10 @@
 src/core/ext/client_config/channel_connectivity.c \
 src/core/ext/client_config/client_channel.c \
 src/core/ext/client_config/client_channel_factory.c \
-src/core/ext/client_config/client_config.c \
 src/core/ext/client_config/client_config_plugin.c \
 src/core/ext/client_config/connector.c \
 src/core/ext/client_config/default_initial_connect_string.c \
+src/core/ext/client_config/http_connect_handshaker.c \
 src/core/ext/client_config/initial_connect_string.c \
 src/core/ext/client_config/lb_policy.c \
 src/core/ext/client_config/lb_policy_factory.c \
@@ -1103,14 +1110,15 @@
 src/core/ext/client_config/resolver.c \
 src/core/ext/client_config/resolver_factory.c \
 src/core/ext/client_config/resolver_registry.c \
+src/core/ext/client_config/resolver_result.c \
 src/core/ext/client_config/subchannel.c \
-src/core/ext/client_config/subchannel_call_holder.c \
 src/core/ext/client_config/subchannel_index.c \
 src/core/ext/client_config/uri_parser.c \
 src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \
 src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \
 src/core/ext/transport/chttp2/client/insecure/channel_create.c \
 src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \
+src/core/ext/lb_policy/grpclb/grpclb.c \
 src/core/ext/lb_policy/grpclb/load_balancer_api.c \
 src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \
 third_party/nanopb/pb_common.c \
@@ -1122,8 +1130,10 @@
 src/core/ext/resolver/sockaddr/sockaddr_resolver.c \
 src/core/ext/load_reporting/load_reporting.c \
 src/core/ext/load_reporting/load_reporting_filter.c \
+src/core/ext/census/base_resources.c \
 src/core/ext/census/context.c \
 src/core/ext/census/gen/census.pb.c \
+src/core/ext/census/gen/trace_context.pb.c \
 src/core/ext/census/grpc_context.c \
 src/core/ext/census/grpc_filter.c \
 src/core/ext/census/grpc_plugin.c \
@@ -1131,6 +1141,8 @@
 src/core/ext/census/mlog.c \
 src/core/ext/census/operation.c \
 src/core/ext/census/placeholders.c \
+src/core/ext/census/resource.c \
+src/core/ext/census/trace_context.c \
 src/core/ext/census/tracing.c \
 src/core/plugin_registry/grpc_plugin_registry.c \
 include/grpc/support/alloc.h \
@@ -1161,25 +1173,24 @@
 include/grpc/support/tls_msvc.h \
 include/grpc/support/tls_pthread.h \
 include/grpc/support/useful.h \
-include/grpc/impl/codegen/alloc.h \
 include/grpc/impl/codegen/atm.h \
 include/grpc/impl/codegen/atm_gcc_atomic.h \
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
-include/grpc/impl/codegen/log.h \
+include/grpc/impl/codegen/gpr_types.h \
 include/grpc/impl/codegen/port_platform.h \
 include/grpc/impl/codegen/slice.h \
-include/grpc/impl/codegen/slice_buffer.h \
 include/grpc/impl/codegen/sync.h \
 include/grpc/impl/codegen/sync_generic.h \
 include/grpc/impl/codegen/sync_posix.h \
 include/grpc/impl/codegen/sync_windows.h \
-include/grpc/impl/codegen/time.h \
 src/core/lib/profiling/timers.h \
 src/core/lib/support/backoff.h \
 src/core/lib/support/block_annotate.h \
 src/core/lib/support/env.h \
+src/core/lib/support/mpscq.h \
 src/core/lib/support/murmur_hash.h \
+src/core/lib/support/percent_encoding.h \
 src/core/lib/support/stack_lockfree.h \
 src/core/lib/support/string.h \
 src/core/lib/support/string_windows.h \
@@ -1206,7 +1217,9 @@
 src/core/lib/support/log_linux.c \
 src/core/lib/support/log_posix.c \
 src/core/lib/support/log_windows.c \
+src/core/lib/support/mpscq.c \
 src/core/lib/support/murmur_hash.c \
+src/core/lib/support/percent_encoding.c \
 src/core/lib/support/slice.c \
 src/core/lib/support/slice_buffer.c \
 src/core/lib/support/stack_lockfree.c \
diff --git a/tools/fuzzer/runners/percent_decode_fuzzer.sh b/tools/fuzzer/runners/percent_decode_fuzzer.sh
new file mode 100644
index 0000000..5a47fa2
--- /dev/null
+++ b/tools/fuzzer/runners/percent_decode_fuzzer.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+# Copyright 2016, 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.
+#
+
+flags="-max_total_time=$runtime -artifact_prefix=fuzzer_output/ -max_len=32 -timeout=120"
+
+
+if [ "$jobs" != "1" ]
+then
+  flags="-jobs=$jobs -workers=$jobs $flags"
+fi
+
+if [ "$config" == "asan-trace-cmp" ]
+then
+  flags="-use_traces=1 $flags"
+fi
+
+bins/$config/percent_decode_fuzzer $flags fuzzer_output test/core/support/percent_decode_corpus
diff --git a/tools/fuzzer/runners/percent_encode_fuzzer.sh b/tools/fuzzer/runners/percent_encode_fuzzer.sh
new file mode 100644
index 0000000..8136ef8
--- /dev/null
+++ b/tools/fuzzer/runners/percent_encode_fuzzer.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+# Copyright 2016, 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.
+#
+
+flags="-max_total_time=$runtime -artifact_prefix=fuzzer_output/ -max_len=32 -timeout=120"
+
+
+if [ "$jobs" != "1" ]
+then
+  flags="-jobs=$jobs -workers=$jobs $flags"
+fi
+
+if [ "$config" == "asan-trace-cmp" ]
+then
+  flags="-use_traces=1 $flags"
+fi
+
+bins/$config/percent_encode_fuzzer $flags fuzzer_output test/core/support/percent_encode_corpus
diff --git a/tools/gce/create_linux_worker.sh b/tools/gce/create_linux_worker.sh
index c41e4d2..013127c 100755
--- a/tools/gce/create_linux_worker.sh
+++ b/tools/gce/create_linux_worker.sh
@@ -42,8 +42,9 @@
 gcloud compute instances create $INSTANCE_NAME \
     --project="$CLOUD_PROJECT" \
     --zone "$ZONE" \
-    --machine-type n1-standard-8 \
-    --image ubuntu-15-10 \
+    --machine-type n1-highmem-8 \
+    --image=ubuntu-1510 \
+    --image-project=grpc-testing \
     --boot-disk-size 1000
 
 echo 'Created GCE instance, waiting 60 seconds for it to come online.'
diff --git a/tools/gce/linux_performance_worker_init.sh b/tools/gce/linux_performance_worker_init.sh
index 23a8f08..ea80d74 100755
--- a/tools/gce/linux_performance_worker_init.sh
+++ b/tools/gce/linux_performance_worker_init.sh
@@ -112,6 +112,11 @@
 sudo apt-get update
 sudo apt-get install -y mono-devel nuget
 
+# The version of nuget that is installed using apt-get is too old to download
+# the System.Interactive.Async.3.0.0 C# dependency. Update to the latest version
+# in order to be able download it.
+sudo nuget update -self
+
 # Ruby dependencies
 gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
 curl -sSL https://get.rvm.io | bash -s stable --ruby
diff --git a/tools/gce/linux_worker_init.sh b/tools/gce/linux_worker_init.sh
index afcf7a5..9230acd 100755
--- a/tools/gce/linux_worker_init.sh
+++ b/tools/gce/linux_worker_init.sh
@@ -34,6 +34,14 @@
 
 set -ex
 
+# Create some swap space
+sudo dd if=/dev/zero of=/swap bs=1024 count=10485760
+sudo chmod 600 /swap
+sudo mkswap /swap
+sudo sed -i '$ a\/swap none swap sw 0 0' /etc/fstab
+sudo swapon -a
+
+# Typical apt-get maintenance
 sudo apt-get update
 
 # Install JRE
diff --git a/tools/gource/gen-all-logs.sh b/tools/gource/gen-all-logs.sh
index 85352c5..2512cc2 100755
--- a/tools/gource/gen-all-logs.sh
+++ b/tools/gource/gen-all-logs.sh
@@ -34,14 +34,13 @@
 
 tmpdir=`mktemp -d`
 mkdir -p $tmpdir/logs
-repos="grpc grpc-common grpc-go grpc-java grpc.github.io grpc-tools homebrew-grpc grpc-docker-library"
+repos="grpc grpc-common grpc-go grpc-java grpc.github.io"
 for repo in $repos
 do
   cd $tmpdir
   git clone https://github.com/grpc/$repo.git
   cd $repo
   gource --output-custom-log $tmpdir/logs/$repo
-  sed -i .backup "s,\|/,\|/$repo/,g" $tmpdir/logs/$repo
+  sed -i "s,|/,|/$repo/,g" $tmpdir/logs/$repo
 done
-rm $tmpdir/logs/*.backup
 cat $tmpdir/logs/* | sort -n > $outdir/all-logs.txt
diff --git a/tools/gource/gource.sh b/tools/gource/gource.sh
index b3dad5d..5529b32 100755
--- a/tools/gource/gource.sh
+++ b/tools/gource/gource.sh
@@ -34,7 +34,7 @@
   --max-file-lag 0.05           \
   --max-files 0                 \
   -e 0.01                       \
-  --hide filenames,dirnames     \
+  --hide filenames,dirnames,mouse,progress     \
   --disable-auto-rotate         \
   --file-filter '/grpc/doc/ref' \
   $*
diff --git a/tools/gource/make-video.sh b/tools/gource/make-video.sh
index 02d79df..cde0437 100755
--- a/tools/gource/make-video.sh
+++ b/tools/gource/make-video.sh
@@ -37,7 +37,7 @@
   --stop-at-end         \
   --output-ppm-stream - \
   $@ |                  \
-ffmpeg                  \
+avconv                  \
   -y                    \
   -r 60                 \
   -f image2pipe         \
diff --git a/tools/grift/Dockerfile b/tools/grift/Dockerfile
new file mode 100644
index 0000000..223ee93
--- /dev/null
+++ b/tools/grift/Dockerfile
@@ -0,0 +1,67 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+FROM ubuntu:14.04
+
+RUN apt-get update && \
+  apt-get install -y \
+  git build-essential \
+  pkg-config flex \
+  bison \
+  libkrb5-dev \
+  libsasl2-dev \
+  libnuma-dev \
+  pkg-config \
+  libssl-dev \
+  autoconf libtool \
+  cmake \
+  libiberty-dev \
+  g++ unzip \
+  curl make automake libtool libboost-dev
+
+# Configure git
+RUN git config --global user.name "Jenkins" && \
+  git config --global user.email "jenkins@grpc"
+
+# Clone gRPC
+RUN git clone https://github.com/grpc/grpc
+
+# Update Submodules
+RUN cd grpc && git submodule update --init
+
+# Install protobuf
+RUN cd grpc/third_party/protobuf && ./autogen.sh && ./configure && \
+  make -j && make check -j && make install && ldconfig
+
+# Install gRPC
+RUN cd grpc && make -j && make install
+
+# Install thrift
+RUN cd grpc/third_party/thrift && git am --signoff < ../../tools/grift/grpc_plugins_generator.patch && \
+  ./bootstrap.sh && ./configure && make -j && make install
\ No newline at end of file
diff --git a/tools/grift/README.md b/tools/grift/README.md
new file mode 100644
index 0000000..7cbbdc5
--- /dev/null
+++ b/tools/grift/README.md
@@ -0,0 +1,26 @@
+Copyright 2016 Google Inc.
+
+#Documentation
+
+grift is integration of [Apache Thrift](https://github.com/apache/thrift.git) Serializer with gRPC.
+
+This integration allows you to use grpc to send thrift messages in C++ and java.
+
+grift uses Compact Protocol to serialize thrift messages. 
+
+##generating grpc plugins for thrift services
+
+###CPP
+```sh
+ $ thrift --gen cpp <thrift-file>
+```
+
+###JAVA
+```sh
+ $ thrift --gen java <thrift-file>
+```
+
+#Installation
+
+Before Installing thrift make sure to apply this [patch](grpc_plugins_generator.patch) to third_party/thrift.
+Go to third_party/thrift and follow the [INSTALLATION](https://github.com/apache/thrift.git) instructions to install thrift with commit id bcad91771b7f0bff28a1cac1981d7ef2b9bcef3c.
\ No newline at end of file
diff --git a/tools/grift/grpc_plugins_generator.patch b/tools/grift/grpc_plugins_generator.patch
new file mode 100644
index 0000000..de82a01
--- /dev/null
+++ b/tools/grift/grpc_plugins_generator.patch
@@ -0,0 +1,2505 @@
+From 0894590b5020c38106d4ebb2291994668c64f9dd Mon Sep 17 00:00:00 2001
+From: chedeti <chedeti@google.com>
+Date: Sun, 31 Jul 2016 15:47:47 -0700
+Subject: [PATCH 1/3] don't build tests
+
+---
+ Makefile.am         | 7 ++-----
+ lib/cpp/Makefile.am | 7 ++-----
+ 2 files changed, 4 insertions(+), 10 deletions(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 10fe49a..d49caac 100755
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -21,10 +21,6 @@ ACLOCAL_AMFLAGS = -I ./aclocal
+ 
+ SUBDIRS = compiler/cpp lib
+ 
+-if WITH_TESTS
+-SUBDIRS += test
+-endif
+-
+ if WITH_TUTORIAL
+ SUBDIRS += tutorial
+ endif
+@@ -117,4 +113,5 @@ EXTRA_DIST = \
+ 	CHANGES \
+ 	NOTICE \
+ 	README.md \
+-	Thrift.podspec
++	Thrift.podspec \
++	test
+diff --git a/lib/cpp/Makefile.am b/lib/cpp/Makefile.am
+index 6fd15d2..7de1fad 100755
+--- a/lib/cpp/Makefile.am
++++ b/lib/cpp/Makefile.am
+@@ -27,10 +27,6 @@ moc__%.cpp: %.h
+ 
+ SUBDIRS = .
+ 
+-if WITH_TESTS
+-SUBDIRS += test
+-endif
+-
+ pkgconfigdir = $(libdir)/pkgconfig
+ 
+ lib_LTLIBRARIES = libthrift.la
+@@ -277,7 +273,8 @@ EXTRA_DIST = \
+              thrift-qt.pc.in \
+              thrift-qt5.pc.in \
+              src/thrift/qt/CMakeLists.txt \
+-             $(WINDOWS_DIST)
++             $(WINDOWS_DIST) \
++             test
+ 
+ style-local:
+ 	$(CPPSTYLE_CMD)
+-- 
+2.8.0.rc3.226.g39d4020
+
+
+From 387e4300bc9d98176a92a7c010621443a538e7f2 Mon Sep 17 00:00:00 2001
+From: chedeti <chedeti@google.com>
+Date: Sun, 31 Jul 2016 16:16:40 -0700
+Subject: [PATCH 2/3] grpc cpp plugins generator with example
+
+---
+ compiler/cpp/src/generate/t_cpp_generator.cc | 489 +++++++++++++++++++++++----
+ tutorial/cpp/CMakeLists.txt                  |  53 ---
+ tutorial/cpp/CppClient.cpp                   |  80 -----
+ tutorial/cpp/CppServer.cpp                   | 181 ----------
+ tutorial/cpp/GriftClient.cpp                 |  93 +++++
+ tutorial/cpp/GriftServer.cpp                 |  93 +++++
+ tutorial/cpp/Makefile.am                     |  66 ++--
+ tutorial/cpp/test.thrift                     |  13 +
+ 8 files changed, 652 insertions(+), 416 deletions(-)
+ delete mode 100644 tutorial/cpp/CMakeLists.txt
+ delete mode 100644 tutorial/cpp/CppClient.cpp
+ delete mode 100644 tutorial/cpp/CppServer.cpp
+ create mode 100644 tutorial/cpp/GriftClient.cpp
+ create mode 100644 tutorial/cpp/GriftServer.cpp
+ create mode 100644 tutorial/cpp/test.thrift
+
+diff --git a/compiler/cpp/src/generate/t_cpp_generator.cc b/compiler/cpp/src/generate/t_cpp_generator.cc
+index 6c04899..1557241 100644
+--- a/compiler/cpp/src/generate/t_cpp_generator.cc
++++ b/compiler/cpp/src/generate/t_cpp_generator.cc
+@@ -162,6 +162,8 @@ public:
+                                  bool specialized = false);
+   void generate_function_helpers(t_service* tservice, t_function* tfunction);
+   void generate_service_async_skeleton(t_service* tservice);
++  void generate_service_stub_interface(t_service* tservice);
++  void generate_service_stub(t_service* tservice);
+ 
+   /**
+    * Serialization constructs
+@@ -883,10 +885,10 @@ void t_cpp_generator::generate_struct_declaration(ofstream& out,
+                                                   bool is_user_struct) {
+   string extends = "";
+   if (is_exception) {
+-    extends = " : public ::apache::thrift::TException";
++    extends = " : public apache::thrift::TException";
+   } else {
+-    if (is_user_struct && !gen_templates_) {
+-      extends = " : public virtual ::apache::thrift::TBase";
++    if (!gen_templates_) {
++      extends = " : public virtual apache::thrift::TBase";
+     }
+   }
+ 
+@@ -1130,9 +1132,15 @@ void t_cpp_generator::generate_struct_definition(ofstream& out,
+   vector<t_field*>::const_iterator m_iter;
+   const vector<t_field*>& members = tstruct->get_members();
+ 
++  string method_prefix = "";
++  if (service_name_ != "") {
++    method_prefix = service_name_ + "::";
++  }
++
+   // Destructor
+   if (tstruct->annotations_.find("final") == tstruct->annotations_.end()) {
+-    force_cpp_out << endl << indent() << tstruct->get_name() << "::~" << tstruct->get_name()
++    force_cpp_out << endl << indent() << method_prefix <<
++    tstruct->get_name() << "::~" << tstruct->get_name()
+                   << "() throw() {" << endl;
+     indent_up();
+ 
+@@ -1145,12 +1153,14 @@ void t_cpp_generator::generate_struct_definition(ofstream& out,
+     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+       if (is_reference((*m_iter))) {
+         std::string type = type_name((*m_iter)->get_type());
+-        out << endl << indent() << "void " << tstruct->get_name() << "::__set_"
++        out << endl << indent() << "void " << method_prefix
++            << tstruct->get_name() << "::__set_"
+             << (*m_iter)->get_name() << "(boost::shared_ptr<"
+             << type_name((*m_iter)->get_type(), false, false) << ">";
+         out << " val) {" << endl;
+       } else {
+-        out << endl << indent() << "void " << tstruct->get_name() << "::__set_"
++        out << endl << indent() << "void " << method_prefix
++            << tstruct->get_name() << "::__set_"
+             << (*m_iter)->get_name() << "(" << type_name((*m_iter)->get_type(), false, true);
+         out << " val) {" << endl;
+       }
+@@ -1177,11 +1187,16 @@ void t_cpp_generator::generate_struct_definition(ofstream& out,
+  * @param tstruct The struct
+  */
+ void t_cpp_generator::generate_struct_reader(ofstream& out, t_struct* tstruct, bool pointers) {
++  string method_prefix = "";
++  if (service_name_ != "") {
++    method_prefix = service_name_ + "::";
++  }
++
+   if (gen_templates_) {
+     out << indent() << "template <class Protocol_>" << endl << indent() << "uint32_t "
+-        << tstruct->get_name() << "::read(Protocol_* iprot) {" << endl;
++        << method_prefix << tstruct->get_name() << "::read(Protocol_* iprot) {" << endl;
+   } else {
+-    indent(out) << "uint32_t " << tstruct->get_name()
++    indent(out) << "uint32_t " << method_prefix << tstruct->get_name()
+                 << "::read(::apache::thrift::protocol::TProtocol* iprot) {" << endl;
+   }
+   indent_up();
+@@ -1301,14 +1316,18 @@ void t_cpp_generator::generate_struct_reader(ofstream& out, t_struct* tstruct, b
+  */
+ void t_cpp_generator::generate_struct_writer(ofstream& out, t_struct* tstruct, bool pointers) {
+   string name = tstruct->get_name();
++  string method_prefix = "";
++  if (service_name_ != "") {
++    method_prefix = service_name_ + "::";
++  }
+   const vector<t_field*>& fields = tstruct->get_sorted_members();
+   vector<t_field*>::const_iterator f_iter;
+ 
+   if (gen_templates_) {
+     out << indent() << "template <class Protocol_>" << endl << indent() << "uint32_t "
+-        << tstruct->get_name() << "::write(Protocol_* oprot) const {" << endl;
++        << method_prefix << tstruct->get_name() << "::write(Protocol_* oprot) const {" << endl;
+   } else {
+-    indent(out) << "uint32_t " << tstruct->get_name()
++    indent(out) << "uint32_t " << method_prefix << tstruct->get_name()
+                 << "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl;
+   }
+   indent_up();
+@@ -1369,14 +1388,18 @@ void t_cpp_generator::generate_struct_result_writer(ofstream& out,
+                                                     t_struct* tstruct,
+                                                     bool pointers) {
+   string name = tstruct->get_name();
++  string method_prefix = "";
++  if (service_name_ != "") {
++    method_prefix = service_name_ + "::";
++  }
+   const vector<t_field*>& fields = tstruct->get_sorted_members();
+   vector<t_field*>::const_iterator f_iter;
+ 
+   if (gen_templates_) {
+     out << indent() << "template <class Protocol_>" << endl << indent() << "uint32_t "
+-        << tstruct->get_name() << "::write(Protocol_* oprot) const {" << endl;
++        << method_prefix << tstruct->get_name() << "::write(Protocol_* oprot) const {" << endl;
+   } else {
+-    indent(out) << "uint32_t " << tstruct->get_name()
++    indent(out) << "uint32_t " << method_prefix << tstruct->get_name()
+                 << "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl;
+   }
+   indent_up();
+@@ -1385,18 +1408,7 @@ void t_cpp_generator::generate_struct_result_writer(ofstream& out,
+ 
+   indent(out) << "xfer += oprot->writeStructBegin(\"" << name << "\");" << endl;
+ 
+-  bool first = true;
+   for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+-    if (first) {
+-      first = false;
+-      out << endl << indent() << "if ";
+-    } else {
+-      out << " else if ";
+-    }
+-
+-    out << "(this->__isset." << (*f_iter)->get_name() << ") {" << endl;
+-
+-    indent_up();
+ 
+     // Write field header
+     out << indent() << "xfer += oprot->writeFieldBegin("
+@@ -1410,9 +1422,6 @@ void t_cpp_generator::generate_struct_result_writer(ofstream& out,
+     }
+     // Write field closer
+     indent(out) << "xfer += oprot->writeFieldEnd();" << endl;
+-
+-    indent_down();
+-    indent(out) << "}";
+   }
+ 
+   // Write the struct map
+@@ -1478,9 +1487,13 @@ void t_cpp_generator::generate_struct_ostream_operator(std::ofstream& out, t_str
+ }
+ 
+ void t_cpp_generator::generate_struct_print_method_decl(std::ofstream& out, t_struct* tstruct) {
++  string method_prefix = "";
++  if (service_name_ != "") {
++    method_prefix = service_name_ + "::";
++  }
+   out << "void ";
+   if (tstruct) {
+-    out << tstruct->get_name() << "::";
++    out << method_prefix << tstruct->get_name() << "::";
+   }
+   out << "printTo(std::ostream& out) const";
+ }
+@@ -1601,11 +1614,13 @@ void t_cpp_generator::generate_exception_what_method(std::ofstream& out, t_struc
+  */
+ void t_cpp_generator::generate_service(t_service* tservice) {
+   string svcname = tservice->get_name();
++  string ns = tservice->get_program()->get_namespace("cpp");
+ 
+   // Make output files
+-  string f_header_name = get_out_dir() + svcname + ".h";
++  string f_header_name = get_out_dir() + svcname + ".grpc.thrift.h";
+   f_header_.open(f_header_name.c_str());
+ 
++
+   // Print header file includes
+   f_header_ << autogen_comment();
+   f_header_ << "#ifndef " << svcname << "_H" << endl << "#define " << svcname << "_H" << endl
+@@ -1621,15 +1636,38 @@ void t_cpp_generator::generate_service(t_service* tservice) {
+     f_header_ << "#include <thrift/async/TAsyncDispatchProcessor.h>" << endl;
+   }
+   f_header_ << "#include <thrift/async/TConcurrentClientSyncInfo.h>" << endl;
++
+   f_header_ << "#include \"" << get_include_prefix(*get_program()) << program_name_ << "_types.h\""
+             << endl;
+ 
+   t_service* extends_service = tservice->get_extends();
+-  if (extends_service != NULL) {
++  if (extends_service) {
+     f_header_ << "#include \"" << get_include_prefix(*(extends_service->get_program()))
+-              << extends_service->get_name() << ".h\"" << endl;
++              << extends_service->get_name() << ".grpc.thrift.h\"" << endl;
+   }
+ 
++
++  f_header_  <<
++      "#include <grpc++/impl/codegen/async_stream.h>" << endl <<
++      "#include <grpc++/impl/codegen/async_unary_call.h>"  << endl <<
++      "#include <grpc++/impl/codegen/thrift_utils.h>" << endl <<
++      "#include <grpc++/impl/codegen/rpc_method.h>"  << endl <<
++      "#include <grpc++/impl/codegen/service_type.h>"  << endl <<
++      "#include <grpc++/impl/codegen/status.h>" << endl <<
++      "#include <grpc++/impl/codegen/stub_options.h>" << endl <<
++      "#include <grpc++/impl/codegen/sync_stream.h>" << endl;
++
++
++  f_header_  <<
++      endl <<
++      "namespace grpc {" << endl <<
++      "class CompletionQueue;"  << endl <<
++      "class Channel;"  << endl <<
++      "class RpcService;"  << endl <<
++      "class ServerCompletionQueue;"  << endl <<
++      "class ServerContext;" << endl <<
++      "}" << endl;
++
+   f_header_ << endl << ns_open_ << endl << endl;
+ 
+   f_header_ << "#ifdef _WIN32\n"
+@@ -1638,10 +1676,13 @@ void t_cpp_generator::generate_service(t_service* tservice) {
+                "#endif\n\n";
+ 
+   // Service implementation file includes
+-  string f_service_name = get_out_dir() + svcname + ".cpp";
++  string f_service_name = get_out_dir() + svcname + ".grpc.thrift.cpp";
+   f_service_.open(f_service_name.c_str());
+   f_service_ << autogen_comment();
+-  f_service_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" << endl;
++
++  f_service_ << "#include \"" <<
++    get_include_prefix(*get_program()) << svcname << ".grpc.thrift.h\"" << endl;
++
+   if (gen_cob_style_) {
+     f_service_ << "#include \"thrift/async/TAsyncChannel.h\"" << endl;
+   }
+@@ -1652,7 +1693,7 @@ void t_cpp_generator::generate_service(t_service* tservice) {
+     string f_service_tcc_name = get_out_dir() + svcname + ".tcc";
+     f_service_tcc_.open(f_service_tcc_name.c_str());
+     f_service_tcc_ << autogen_comment();
+-    f_service_tcc_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\""
++    f_service_tcc_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".grpc.thrift.h\""
+                    << endl;
+ 
+     f_service_tcc_ << "#ifndef " << svcname << "_TCC" << endl << "#define " << svcname << "_TCC"
+@@ -1663,19 +1704,69 @@ void t_cpp_generator::generate_service(t_service* tservice) {
+     }
+   }
+ 
++  f_service_ <<
++    endl <<
++    "#include <grpc++/impl/codegen/async_stream.h>" << endl <<
++    "#include <grpc++/impl/codegen/async_unary_call.h>" << endl <<
++    "#include <grpc++/impl/codegen/channel_interface.h>" << endl <<
++    "#include <grpc++/impl/codegen/client_unary_call.h>" << endl <<
++    "#include <grpc++/impl/codegen/method_handler_impl.h>" << endl <<
++    "#include <grpc++/impl/codegen/rpc_service_method.h>" << endl <<
++    "#include <grpc++/impl/codegen/service_type.h>" << endl <<
++    "#include <grpc++/impl/codegen/sync_stream.h>" << endl <<
++    endl;
++
+   f_service_ << endl << ns_open_ << endl << endl;
+   f_service_tcc_ << endl << ns_open_ << endl << endl;
+ 
++  vector<t_function*> functions = tservice->get_functions();
++  vector<t_function*>::iterator f_iter;
++
++  f_service_ <<
++    "static const char* " << service_name_ << "_method_names[] = {" << endl;
++
++
++  indent_up();
++
++  for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    f_service_ <<
++      indent() << "\"/" << ns << "." << service_name_ << "/" << (*f_iter)->get_name() << "\"," << endl;
++  }
++
++
++  t_service* service_iter = extends_service;  
++  while (service_iter) {
++    vector<t_function*> functions = service_iter->get_functions();
++    vector<t_function*>::iterator f_iter;
++
++    for ( f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      f_service_ <<
++      indent() << "\"/" << service_iter->get_program()->get_namespace("cpp") <<
++      "." << service_iter->get_name() << "/" << (*f_iter)->get_name() << "\"," << endl;
++    }
++    service_iter = service_iter->get_extends();
++  }
++
++  indent_down();
++  f_service_ <<
++  "};" << endl;
++
++  // Generate service class
++  if ( extends_service) {
++    f_header_ << "class " << service_name_ << " : public " <<
++      type_name(extends_service) << " {" << endl <<
++      "public:" << endl;
++  }
++  else {
++    f_header_ << "class " << service_name_ << "{" << endl <<
++      "public:" << endl;
++  }
++
+   // Generate all the components
+-  generate_service_interface(tservice, "");
+-  generate_service_interface_factory(tservice, "");
+-  generate_service_null(tservice, "");
+   generate_service_helpers(tservice);
+-  generate_service_client(tservice, "");
+-  generate_service_processor(tservice, "");
+-  generate_service_multiface(tservice);
+-  generate_service_skeleton(tservice);
+-  generate_service_client(tservice, "Concurrent");
++  generate_service_interface(tservice, "");
++  generate_service_stub_interface(tservice);
++  generate_service_stub(tservice);
+ 
+   // Generate all the cob components
+   if (gen_cob_style_) {
+@@ -1688,10 +1779,14 @@ void t_cpp_generator::generate_service(t_service* tservice) {
+     generate_service_async_skeleton(tservice);
+   }
+ 
++   // Close service class
++  f_header_ << "};" << endl;
++
+   f_header_ << "#ifdef _WIN32\n"
+                "  #pragma warning( pop )\n"
+                "#endif\n\n";
+ 
++
+   // Close the namespace
+   f_service_ << ns_close_ << endl << endl;
+   f_service_tcc_ << ns_close_ << endl << endl;
+@@ -1729,15 +1824,11 @@ void t_cpp_generator::generate_service_helpers(t_service* tservice) {
+     string name_orig = ts->get_name();
+ 
+     // TODO(dreiss): Why is this stuff not in generate_function_helpers?
+-    ts->set_name(tservice->get_name() + "_" + (*f_iter)->get_name() + "_args");
++    ts->set_name((*f_iter)->get_name() + "Req");
+     generate_struct_declaration(f_header_, ts, false);
+-    generate_struct_definition(out, f_service_, ts, false);
++    generate_struct_definition(out, f_service_, ts, true);
+     generate_struct_reader(out, ts);
+     generate_struct_writer(out, ts);
+-    ts->set_name(tservice->get_name() + "_" + (*f_iter)->get_name() + "_pargs");
+-    generate_struct_declaration(f_header_, ts, false, true, false, true);
+-    generate_struct_definition(out, f_service_, ts, false);
+-    generate_struct_writer(out, ts, true);
+     ts->set_name(name_orig);
+ 
+     generate_function_helpers(tservice, *f_iter);
+@@ -1745,13 +1836,218 @@ void t_cpp_generator::generate_service_helpers(t_service* tservice) {
+ }
+ 
+ /**
++ *  Generates a service Stub Interface
++ *
++ * @param tservice The service to generate a stub for.
++ *
++ */
++void t_cpp_generator::generate_service_stub_interface(t_service* tservice) {
++
++    string extends = "";
++    if (tservice->get_extends()) {
++      extends = " : virtual public " + type_name(tservice->get_extends()) + "::StubInterface";
++    }
++
++    f_header_ <<
++    endl <<
++    "class StubInterface " << extends << " {" << endl;
++    indent_up();
++    f_header_ <<
++    " public:" << endl <<
++    indent() << "virtual ~StubInterface() {}" << endl;
++
++    vector<t_function*> functions = tservice->get_functions();
++    vector<t_function*>::iterator f_iter;
++    for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      string function_name = (*f_iter)->get_name();
++      f_header_ <<
++        indent() << "virtual ::grpc::Status " << function_name <<
++        "(::grpc::ClientContext* context, const " << function_name <<
++        "Req& request, " << function_name << "Resp* response) = 0;" << endl;
++    }
++    indent_down();
++    f_header_ <<
++      "};" << endl << endl;
++
++}
++void t_cpp_generator::generate_service_stub(t_service* tservice) {
++    f_header_ <<
++    endl <<
++    "class Stub : public StubInterface {" <<
++    endl;
++
++    indent_up();
++    f_header_ <<
++    " public:" << endl <<
++    indent() << "Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel);" <<
++    endl;
++
++    vector<t_function*> functions = tservice->get_functions();
++    vector<t_function*>::iterator f_iter;
++    for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      string function_name = (*f_iter)->get_name();
++      f_header_ <<
++        indent() << "::grpc::Status " << function_name <<
++        "(::grpc::ClientContext* context, const " << function_name <<
++        "Req& request, " << function_name << "Resp* response) override;" << endl;
++    }
++
++    t_service* extends_service = tservice->get_extends();
++    t_service* service_iter = extends_service;
++    while (service_iter) {
++      // generate inherited methods
++      vector<t_function*> functions = service_iter->get_functions();
++      vector<t_function*>::iterator f_iter;
++      for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++        string function_name = (*f_iter)->get_name();
++        f_header_ <<
++          indent() << "::grpc::Status " << function_name <<
++          "(::grpc::ClientContext* context, const " << function_name <<
++          "Req& request, " << function_name << "Resp* response) override;" << endl;
++      }
++      service_iter = service_iter->get_extends();
++    }
++
++    f_header_ <<
++    endl <<
++    " private:" << endl <<
++    indent() << "std::shared_ptr< ::grpc::ChannelInterface> channel_;" <<
++    endl;
++
++    for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      f_header_ <<
++        indent() << "const ::grpc::RpcMethod rpcmethod_" << (*f_iter)->get_name() << "_;" << endl;
++    }
++
++    service_iter = extends_service;
++    while (service_iter) {
++      // generate inherited methods
++      vector<t_function*> functions = service_iter->get_functions();
++      vector<t_function*>::iterator f_iter;
++      for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++        f_header_ <<
++          indent() << "const ::grpc::RpcMethod rpcmethod_" << (*f_iter)->get_name() << "_;" << endl;
++      }
++      service_iter = service_iter->get_extends();
++    }
++
++    indent_down();
++    f_header_ <<
++      "};" << endl << endl;
++
++    // generate the implementaion of Stub
++    f_service_ <<
++      endl <<
++      service_name_ << "::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel)" << endl;
++
++    indent_up();
++    f_service_ <<
++    indent() << ": channel_(channel)" << endl;
++    int i=0;
++    for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter , ++i) {
++      f_service_ <<
++        indent() <<
++        ", rpcmethod_" << (*f_iter)->get_name() << "_(" <<
++        service_name_ << "_method_names[" << i << "], ::grpc::RpcMethod::NORMAL_RPC, channel)" << endl;
++    }
++
++    service_iter = extends_service;
++    while (service_iter) {
++      // generate inherited methods
++      vector<t_function*> functions = service_iter->get_functions();
++      vector<t_function*>::iterator f_iter;
++      for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter, ++i) {
++        f_service_ <<
++        indent() <<
++        ", rpcmethod_" << (*f_iter)->get_name() << "_(" <<
++        service_name_ << "_method_names[" << i << "], ::grpc::RpcMethod::NORMAL_RPC, channel)" << endl;
++      }
++      service_iter = service_iter->get_extends();
++    }
++    f_service_ <<
++    indent() << "{}" << endl;
++    indent_down();
++
++    // generate NewStub
++    f_header_ <<
++    endl <<
++    "static std::unique_ptr<Stub> NewStub(const std::shared_ptr\
++    < ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options = ::grpc::StubOptions());" <<
++    endl;
++
++    // generate NewStub Implementation
++    f_service_ <<
++    endl <<
++    "std::unique_ptr< " << service_name_ << "::Stub> " << service_name_ << "::NewStub(const std::shared_ptr\
++    < ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) {" << endl;
++
++    indent_up();
++    f_service_ <<
++    indent() << "std::unique_ptr< " << service_name_ << "::Stub> stub(new " << service_name_ <<
++    "::Stub(channel));" << endl <<
++    indent() << "return stub;" << endl;
++    indent_down();
++    f_service_ <<
++    "}" << endl;
++
++    // generate stub methods
++    for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      string function_name = (*f_iter)->get_name();
++      f_service_ <<
++        endl <<
++        "::grpc::Status " << service_name_ << "::Stub::" <<  function_name <<
++        "(::grpc::ClientContext* context, const " << service_name_ << "::" <<
++        function_name << "Req& request, " << service_name_ << "::" <<
++        function_name << "Resp* response) {" << endl;
++
++      indent_up();
++      f_service_ <<
++      indent() << "return ::grpc::BlockingUnaryCall(channel_.get(), rpcmethod_" <<
++      function_name << "_, context, request, response);" << endl;
++      indent_down();
++
++      f_service_ <<
++      "}" << endl;
++
++    }
++
++    service_iter = extends_service;
++    while (service_iter) {
++      vector<t_function*> functions = service_iter->get_functions();
++      vector<t_function*>::iterator f_iter;
++      for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++        string function_name = (*f_iter)->get_name();
++        f_service_ <<
++          endl <<
++          "::grpc::Status " << service_name_ << "::Stub::" <<  function_name <<
++          "(::grpc::ClientContext* context, const " << service_name_ << "::" <<
++          function_name << "Req& request, " << service_name_ << "::" <<
++          function_name << "Resp* response) {" << endl;
++
++        indent_up();
++        f_service_ <<
++        indent() << "return ::grpc::BlockingUnaryCall(channel_.get(), rpcmethod_" <<
++        function_name << "_, context, request, response);" << endl;
++        indent_down();
++
++        f_service_ <<
++        "}" << endl;
++
++      }
++      service_iter = service_iter->get_extends();
++    }
++
++}
++
++
++/**
+  * Generates a service interface definition.
+  *
+  * @param tservice The service to generate a header definition for
+  */
+ void t_cpp_generator::generate_service_interface(t_service* tservice, string style) {
+ 
+-  string service_if_name = service_name_ + style + "If";
++  string service_if_name = "Service";
+   if (style == "CobCl") {
+     // Forward declare the client.
+     string client_name = service_name_ + "CobClient";
+@@ -1764,13 +2060,15 @@ void t_cpp_generator::generate_service_interface(t_service* tservice, string sty
+   }
+ 
+   string extends = "";
+-  if (tservice->get_extends() != NULL) {
+-    extends = " : virtual public " + type_name(tservice->get_extends()) + style + "If";
++  if (tservice->get_extends()) {
++    extends = " : virtual public " + type_name(tservice->get_extends()) + style + "::Service";
+     if (style == "CobCl" && gen_templates_) {
+       // TODO(simpkins): If gen_templates_ is enabled, we currently assume all
+       // parent services were also generated with templates enabled.
+       extends += "T<Protocol_>";
+     }
++  } else {
++    extends = " : public ::grpc::Service";
+   }
+ 
+   if (style == "CobCl" && gen_templates_) {
+@@ -1778,7 +2076,9 @@ void t_cpp_generator::generate_service_interface(t_service* tservice, string sty
+   }
+   f_header_ << "class " << service_if_name << extends << " {" << endl << " public:" << endl;
+   indent_up();
+-  f_header_ << indent() << "virtual ~" << service_if_name << "() {}" << endl;
++
++  f_header_ << indent() << "Service();" << endl;
++  f_header_ << indent() << "virtual ~Service();" << endl;
+ 
+   vector<t_function*> functions = tservice->get_functions();
+   vector<t_function*>::iterator f_iter;
+@@ -1786,7 +2086,12 @@ void t_cpp_generator::generate_service_interface(t_service* tservice, string sty
+     if ((*f_iter)->has_doc())
+       f_header_ << endl;
+     generate_java_doc(f_header_, *f_iter);
+-    f_header_ << indent() << "virtual " << function_signature(*f_iter, style) << " = 0;" << endl;
++
++    string function_name = (*f_iter)->get_name();
++    f_header_ <<
++      indent() << "virtual ::grpc::Status " << function_name <<
++      "(::grpc::ServerContext* context, const "<< function_name <<
++      "Req* request, "<< function_name << "Resp* response);" << endl;
+   }
+   indent_down();
+   f_header_ << "};" << endl << endl;
+@@ -1797,6 +2102,66 @@ void t_cpp_generator::generate_service_interface(t_service* tservice, string sty
+     f_header_ << "typedef " << service_if_name << "< ::apache::thrift::protocol::TProtocol> "
+               << service_name_ << style << "If;" << endl << endl;
+   }
++
++   // generate the service interface implementations
++
++    f_service_ <<
++    endl <<
++    service_name_ << "::Service::Service() {" << endl;
++    indent_up();
++    f_service_ <<
++    indent() << "(void)" << service_name_ << "_method_names;" << endl;
++    uint32_t i=0;
++    for(i=0;i<functions.size(); i++) {
++      string function_name = functions[i]->get_name();
++      f_service_ <<
++        endl <<
++        indent() << "AddMethod(new ::grpc::RpcServiceMethod(" << endl;
++        indent_up();
++
++      f_service_ <<
++        indent() << service_name_ << "_method_names[" << i << "]," << endl <<
++        indent() << "::grpc::RpcMethod::NORMAL_RPC," << endl <<
++        indent() << "new ::grpc::RpcMethodHandler< " << service_name_ << "::Service, " <<
++        service_name_ << "::" << function_name << "Req, " << service_name_ << "::" <<
++        function_name << "Resp>(" << endl;
++
++      indent_up();
++      f_service_ <<
++        indent() << "std::mem_fn(&" << service_name_ << "::Service::" << function_name << "), this)));" << endl;
++
++      indent_down();
++      indent_down();
++    }
++
++    indent_down();
++    f_service_ <<
++    "}" << endl;
++
++    f_service_ <<
++    endl <<
++    service_name_ << "::Service::~Service() {" << endl <<
++    "}" << endl;
++
++    for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      string function_name = (*f_iter)->get_name();
++      f_service_ <<
++        endl <<
++        "::grpc::Status " << service_name_ << "::Service::" << function_name <<
++        "(::grpc::ServerContext* context, const " << service_name_ << "::" << function_name <<
++        "Req* request, " << service_name_ << "::" << function_name << "Resp* response) {" << endl;
++      indent_up();
++      f_service_ <<
++      indent() << "(void) context;" << endl <<
++      indent() << "(void) request;" << endl <<
++      indent() << "(void) response;" << endl <<
++      indent() << "return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED,\"\");" << endl;
++      indent_down();
++
++      f_service_ <<
++      "}" << endl;
++    }
++
+ }
+ 
+ /**
+@@ -3095,7 +3460,7 @@ void t_cpp_generator::generate_function_helpers(t_service* tservice, t_function*
+ 
+   std::ofstream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
+ 
+-  t_struct result(program_, tservice->get_name() + "_" + tfunction->get_name() + "_result");
++  t_struct result(program_, tfunction->get_name() + "Resp");
+   t_field success(tfunction->get_returntype(), "success", 0);
+   if (!tfunction->get_returntype()->is_void()) {
+     result.append(&success);
+@@ -3109,17 +3474,9 @@ void t_cpp_generator::generate_function_helpers(t_service* tservice, t_function*
+   }
+ 
+   generate_struct_declaration(f_header_, &result, false);
+-  generate_struct_definition(out, f_service_, &result, false);
++  generate_struct_definition(out, f_service_, &result, true);
+   generate_struct_reader(out, &result);
+   generate_struct_result_writer(out, &result);
+-
+-  result.set_name(tservice->get_name() + "_" + tfunction->get_name() + "_presult");
+-  generate_struct_declaration(f_header_, &result, false, true, true, gen_cob_style_);
+-  generate_struct_definition(out, f_service_, &result, false);
+-  generate_struct_reader(out, &result, true);
+-  if (gen_cob_style_) {
+-    generate_struct_writer(out, &result, true);
+-  }
+ }
+ 
+ /**
+@@ -3162,8 +3519,8 @@ void t_cpp_generator::generate_process_function(t_service* tservice,
+         << endl;
+     scope_up(out);
+ 
+-    string argsname = tservice->get_name() + "_" + tfunction->get_name() + "_args";
+-    string resultname = tservice->get_name() + "_" + tfunction->get_name() + "_result";
++    string argsname = tfunction->get_name() + "Req";
++    string resultname = tfunction->get_name() + "Resp";
+ 
+     if (tfunction->is_oneway() && !unnamed_oprot_seqid) {
+       out << indent() << "(void) seqid;" << endl << indent() << "(void) oprot;" << endl;
+@@ -3320,7 +3677,7 @@ void t_cpp_generator::generate_process_function(t_service* tservice,
+       out << indent() << "(void) seqid;" << endl << indent() << "(void) oprot;" << endl;
+     }
+ 
+-    out << indent() << tservice->get_name() + "_" + tfunction->get_name() << "_args args;" << endl
++    out << indent() << tfunction->get_name() << "Req args;" << endl
+         << indent() << "void* ctx = NULL;" << endl << indent()
+         << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+         << "  ctx = this->eventHandler_->getContext(" << service_func_name << ", NULL);" << endl
+@@ -3487,7 +3844,7 @@ void t_cpp_generator::generate_process_function(t_service* tservice,
+           << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl;
+ 
+       // Throw the TDelayedException, and catch the result
+-      out << indent() << tservice->get_name() << "_" << tfunction->get_name() << "_result result;"
++      out << indent() << tfunction->get_name() << "Resp result;"
+           << endl << endl << indent() << "try {" << endl;
+       indent_up();
+       out << indent() << "_throw->throw_it();" << endl << indent() << "return cob(false);"
+diff --git a/tutorial/cpp/CMakeLists.txt b/tutorial/cpp/CMakeLists.txt
+deleted file mode 100644
+index 8a3d085..0000000
+--- a/tutorial/cpp/CMakeLists.txt
++++ /dev/null
+@@ -1,53 +0,0 @@
+-#
+-# Licensed to the Apache Software Foundation (ASF) under one
+-# or more contributor license agreements. See the NOTICE file
+-# distributed with this work for additional information
+-# regarding copyright ownership. The ASF licenses this file
+-# to you under the Apache License, Version 2.0 (the
+-# "License"); you may not use this file except in compliance
+-# with the License. You may obtain a copy of the License at
+-#
+-#   http://www.apache.org/licenses/LICENSE-2.0
+-#
+-# Unless required by applicable law or agreed to in writing,
+-# software distributed under the License is distributed on an
+-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-# KIND, either express or implied. See the License for the
+-# specific language governing permissions and limitations
+-# under the License.
+-#
+-
+-find_package(Boost 1.53.0 REQUIRED)
+-include_directories(SYSTEM "${Boost_INCLUDE_DIRS}")
+-
+-#Make sure gen-cpp files can be included
+-include_directories("${CMAKE_CURRENT_BINARY_DIR}")
+-include_directories("${CMAKE_CURRENT_BINARY_DIR}/gen-cpp")
+-include_directories("${PROJECT_SOURCE_DIR}/lib/cpp/src")
+-
+-include(ThriftMacros)
+-
+-set(tutorialgencpp_SOURCES 
+-    gen-cpp/Calculator.cpp
+-    gen-cpp/SharedService.cpp
+-    gen-cpp/shared_constants.cpp
+-    gen-cpp/shared_types.cpp
+-    gen-cpp/tutorial_constants.cpp
+-    gen-cpp/tutorial_types.cpp
+-)
+-add_library(tutorialgencpp STATIC ${tutorialgencpp_SOURCES})
+-LINK_AGAINST_THRIFT_LIBRARY(tutorialgencpp thrift)
+-
+-add_custom_command(OUTPUT gen-cpp/Calculator.cpp gen-cpp/SharedService.cpp gen-cpp/shared_constants.cpp gen-cpp/shared_types.cpp gen-cpp/tutorial_constants.cpp gen-cpp/tutorial_types.cpp
+-    COMMAND ${THRIFT_COMPILER} --gen cpp -r ${PROJECT_SOURCE_DIR}/tutorial/tutorial.thrift
+-)
+-
+-add_executable(TutorialServer CppServer.cpp)
+-target_link_libraries(TutorialServer tutorialgencpp)
+-LINK_AGAINST_THRIFT_LIBRARY(TutorialServer thrift)
+-target_link_libraries(TutorialServer ${ZLIB_LIBRARIES})
+-
+-add_executable(TutorialClient CppClient.cpp)
+-target_link_libraries(TutorialClient tutorialgencpp)
+-LINK_AGAINST_THRIFT_LIBRARY(TutorialClient thrift)
+-target_link_libraries(TutorialClient ${ZLIB_LIBRARIES})
+diff --git a/tutorial/cpp/CppClient.cpp b/tutorial/cpp/CppClient.cpp
+deleted file mode 100644
+index 2763fee..0000000
+--- a/tutorial/cpp/CppClient.cpp
++++ /dev/null
+@@ -1,80 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one
+- * or more contributor license agreements. See the NOTICE file
+- * distributed with this work for additional information
+- * regarding copyright ownership. The ASF licenses this file
+- * to you under the Apache License, Version 2.0 (the
+- * "License"); you may not use this file except in compliance
+- * with the License. You may obtain a copy of the License at
+- *
+- *   http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing,
+- * software distributed under the License is distributed on an
+- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+- * KIND, either express or implied. See the License for the
+- * specific language governing permissions and limitations
+- * under the License.
+- */
+-
+-#include <iostream>
+-
+-#include <thrift/protocol/TBinaryProtocol.h>
+-#include <thrift/transport/TSocket.h>
+-#include <thrift/transport/TTransportUtils.h>
+-
+-#include "../gen-cpp/Calculator.h"
+-
+-using namespace std;
+-using namespace apache::thrift;
+-using namespace apache::thrift::protocol;
+-using namespace apache::thrift::transport;
+-
+-using namespace tutorial;
+-using namespace shared;
+-
+-int main() {
+-  boost::shared_ptr<TTransport> socket(new TSocket("localhost", 9090));
+-  boost::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
+-  boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
+-  CalculatorClient client(protocol);
+-
+-  try {
+-    transport->open();
+-
+-    client.ping();
+-    cout << "ping()" << endl;
+-
+-    cout << "1 + 1 = " << client.add(1, 1) << endl;
+-
+-    Work work;
+-    work.op = Operation::DIVIDE;
+-    work.num1 = 1;
+-    work.num2 = 0;
+-
+-    try {
+-      client.calculate(1, work);
+-      cout << "Whoa? We can divide by zero!" << endl;
+-    } catch (InvalidOperation& io) {
+-      cout << "InvalidOperation: " << io.why << endl;
+-      // or using generated operator<<: cout << io << endl;
+-      // or by using std::exception native method what(): cout << io.what() << endl;
+-    }
+-
+-    work.op = Operation::SUBTRACT;
+-    work.num1 = 15;
+-    work.num2 = 10;
+-    int32_t diff = client.calculate(1, work);
+-    cout << "15 - 10 = " << diff << endl;
+-
+-    // Note that C++ uses return by reference for complex types to avoid
+-    // costly copy construction
+-    SharedStruct ss;
+-    client.getStruct(ss, 1);
+-    cout << "Received log: " << ss << endl;
+-
+-    transport->close();
+-  } catch (TException& tx) {
+-    cout << "ERROR: " << tx.what() << endl;
+-  }
+-}
+diff --git a/tutorial/cpp/CppServer.cpp b/tutorial/cpp/CppServer.cpp
+deleted file mode 100644
+index eafffa9..0000000
+--- a/tutorial/cpp/CppServer.cpp
++++ /dev/null
+@@ -1,181 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one
+- * or more contributor license agreements. See the NOTICE file
+- * distributed with this work for additional information
+- * regarding copyright ownership. The ASF licenses this file
+- * to you under the Apache License, Version 2.0 (the
+- * "License"); you may not use this file except in compliance
+- * with the License. You may obtain a copy of the License at
+- *
+- *   http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing,
+- * software distributed under the License is distributed on an
+- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+- * KIND, either express or implied. See the License for the
+- * specific language governing permissions and limitations
+- * under the License.
+- */
+-
+-#include <thrift/concurrency/ThreadManager.h>
+-#include <thrift/concurrency/PlatformThreadFactory.h>
+-#include <thrift/protocol/TBinaryProtocol.h>
+-#include <thrift/server/TSimpleServer.h>
+-#include <thrift/server/TThreadPoolServer.h>
+-#include <thrift/server/TThreadedServer.h>
+-#include <thrift/transport/TServerSocket.h>
+-#include <thrift/transport/TSocket.h>
+-#include <thrift/transport/TTransportUtils.h>
+-#include <thrift/TToString.h>
+-
+-#include <boost/make_shared.hpp>
+-
+-#include <iostream>
+-#include <stdexcept>
+-#include <sstream>
+-
+-#include "../gen-cpp/Calculator.h"
+-
+-using namespace std;
+-using namespace apache::thrift;
+-using namespace apache::thrift::concurrency;
+-using namespace apache::thrift::protocol;
+-using namespace apache::thrift::transport;
+-using namespace apache::thrift::server;
+-
+-using namespace tutorial;
+-using namespace shared;
+-
+-class CalculatorHandler : public CalculatorIf {
+-public:
+-  CalculatorHandler() {}
+-
+-  void ping() { cout << "ping()" << endl; }
+-
+-  int32_t add(const int32_t n1, const int32_t n2) {
+-    cout << "add(" << n1 << ", " << n2 << ")" << endl;
+-    return n1 + n2;
+-  }
+-
+-  int32_t calculate(const int32_t logid, const Work& work) {
+-    cout << "calculate(" << logid << ", " << work << ")" << endl;
+-    int32_t val;
+-
+-    switch (work.op) {
+-    case Operation::ADD:
+-      val = work.num1 + work.num2;
+-      break;
+-    case Operation::SUBTRACT:
+-      val = work.num1 - work.num2;
+-      break;
+-    case Operation::MULTIPLY:
+-      val = work.num1 * work.num2;
+-      break;
+-    case Operation::DIVIDE:
+-      if (work.num2 == 0) {
+-        InvalidOperation io;
+-        io.whatOp = work.op;
+-        io.why = "Cannot divide by 0";
+-        throw io;
+-      }
+-      val = work.num1 / work.num2;
+-      break;
+-    default:
+-      InvalidOperation io;
+-      io.whatOp = work.op;
+-      io.why = "Invalid Operation";
+-      throw io;
+-    }
+-
+-    SharedStruct ss;
+-    ss.key = logid;
+-    ss.value = to_string(val);
+-
+-    log[logid] = ss;
+-
+-    return val;
+-  }
+-
+-  void getStruct(SharedStruct& ret, const int32_t logid) {
+-    cout << "getStruct(" << logid << ")" << endl;
+-    ret = log[logid];
+-  }
+-
+-  void zip() { cout << "zip()" << endl; }
+-
+-protected:
+-  map<int32_t, SharedStruct> log;
+-};
+-
+-/*
+-  CalculatorIfFactory is code generated.
+-  CalculatorCloneFactory is useful for getting access to the server side of the
+-  transport.  It is also useful for making per-connection state.  Without this
+-  CloneFactory, all connections will end up sharing the same handler instance.
+-*/
+-class CalculatorCloneFactory : virtual public CalculatorIfFactory {
+- public:
+-  virtual ~CalculatorCloneFactory() {}
+-  virtual CalculatorIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo)
+-  {
+-    boost::shared_ptr<TSocket> sock = boost::dynamic_pointer_cast<TSocket>(connInfo.transport);
+-    cout << "Incoming connection\n";
+-    cout << "\tSocketInfo: "  << sock->getSocketInfo() << "\n";
+-    cout << "\tPeerHost: "    << sock->getPeerHost() << "\n";
+-    cout << "\tPeerAddress: " << sock->getPeerAddress() << "\n";
+-    cout << "\tPeerPort: "    << sock->getPeerPort() << "\n";
+-    return new CalculatorHandler;
+-  }
+-  virtual void releaseHandler( ::shared::SharedServiceIf* handler) {
+-    delete handler;
+-  }
+-};
+-
+-int main() {
+-  TThreadedServer server(
+-    boost::make_shared<CalculatorProcessorFactory>(boost::make_shared<CalculatorCloneFactory>()),
+-    boost::make_shared<TServerSocket>(9090), //port
+-    boost::make_shared<TBufferedTransportFactory>(),
+-    boost::make_shared<TBinaryProtocolFactory>());
+-
+-  /*
+-  // if you don't need per-connection state, do the following instead
+-  TThreadedServer server(
+-    boost::make_shared<CalculatorProcessor>(boost::make_shared<CalculatorHandler>()),
+-    boost::make_shared<TServerSocket>(9090), //port
+-    boost::make_shared<TBufferedTransportFactory>(),
+-    boost::make_shared<TBinaryProtocolFactory>());
+-  */
+-
+-  /**
+-   * Here are some alternate server types...
+-
+-  // This server only allows one connection at a time, but spawns no threads
+-  TSimpleServer server(
+-    boost::make_shared<CalculatorProcessor>(boost::make_shared<CalculatorHandler>()),
+-    boost::make_shared<TServerSocket>(9090),
+-    boost::make_shared<TBufferedTransportFactory>(),
+-    boost::make_shared<TBinaryProtocolFactory>());
+-
+-  const int workerCount = 4;
+-
+-  boost::shared_ptr<ThreadManager> threadManager =
+-    ThreadManager::newSimpleThreadManager(workerCount);
+-  threadManager->threadFactory(
+-    boost::make_shared<PlatformThreadFactory>());
+-  threadManager->start();
+-
+-  // This server allows "workerCount" connection at a time, and reuses threads
+-  TThreadPoolServer server(
+-    boost::make_shared<CalculatorProcessorFactory>(boost::make_shared<CalculatorCloneFactory>()),
+-    boost::make_shared<TServerSocket>(9090),
+-    boost::make_shared<TBufferedTransportFactory>(),
+-    boost::make_shared<TBinaryProtocolFactory>(),
+-    threadManager);
+-  */
+-
+-  cout << "Starting the server..." << endl;
+-  server.serve();
+-  cout << "Done." << endl;
+-  return 0;
+-}
+diff --git a/tutorial/cpp/GriftClient.cpp b/tutorial/cpp/GriftClient.cpp
+new file mode 100644
+index 0000000..647a683
+--- /dev/null
++++ b/tutorial/cpp/GriftClient.cpp
+@@ -0,0 +1,93 @@
++/*
++ *
++ * Copyright 2016, 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 <iostream>
++#include <memory>
++#include <string>
++
++#include <grpc++/grpc++.h>
++
++#include "gen-cpp/Greeter.grpc.thrift.h"
++
++using grpc::Channel;
++using grpc::ClientContext;
++using grpc::Status;
++using test::Greeter;
++
++class GreeterClient {
++ public:
++  GreeterClient(std::shared_ptr<Channel> channel)
++      : stub_(Greeter::NewStub(channel)) {}
++
++  // Assembles the client's payload, sends it and presents the response back
++  // from the server.
++  std::string SayHello(const std::string& user) {
++    // Data we are sending to the server.
++    Greeter::SayHelloReq req;
++    req.request.name = user;
++
++    // Container for the data we expect from the server.
++    Greeter::SayHelloResp reply;
++
++    // Context for the client. It could be used to convey extra information to
++    // the server and/or tweak certain RPC behaviors.
++    ClientContext context;
++
++    // The actual RPC.
++    Status status = stub_->SayHello(&context, req, &reply);
++
++    // Act upon its status.
++    if (status.ok()) {
++      return reply.success.message;
++    } else {
++      return "RPC failed";
++    }
++  }
++
++ private:
++  std::unique_ptr<Greeter::Stub> stub_;
++};
++
++int main() {
++  // Instantiate the client. It requires a channel, out of which the actual RPCs
++  // are created. This channel models a connection to an endpoint (in this case,
++  // localhost at port 50051). We indicate that the channel isn't authenticated
++  // (use of InsecureChannelCredentials()).
++  GreeterClient greeter(grpc::CreateChannel(
++      "localhost:50051", grpc::InsecureChannelCredentials()));
++  std::string user("world");
++  std::string reply = greeter.SayHello(user);
++  std::cout << "Greeter received: " << reply << std::endl;
++
++  return 0;
++}
+diff --git a/tutorial/cpp/GriftServer.cpp b/tutorial/cpp/GriftServer.cpp
+new file mode 100644
+index 0000000..7c01606
+--- /dev/null
++++ b/tutorial/cpp/GriftServer.cpp
+@@ -0,0 +1,93 @@
++/*
++ *
++ * Copyright 2016, 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 <iostream>
++#include <memory>
++#include <string>
++
++#include <grpc++/grpc++.h>
++
++#include "gen-cpp/Greeter.grpc.thrift.h"
++#include <grpc++/server_builder.h>
++
++using grpc::Server;
++using grpc::ServerBuilder;
++using grpc::ServerContext;
++using grpc::Status;
++using test::Greeter;
++
++// Logic and data behind the server's behavior.
++class GreeterServiceImpl final : public Greeter::Service {
++ public:
++  ~GreeterServiceImpl() {
++    // shutdown server
++    server->Shutdown();
++  }
++
++  Status SayHello(ServerContext* context,const Greeter::SayHelloReq* request,
++                  Greeter::SayHelloResp* reply) override {
++    std::string prefix("Hello ");
++
++    reply->success.message = prefix + request->request.name;
++
++    return Status::OK;
++  }
++
++  void RunServer() {
++    std::string server_address("0.0.0.0:50051");
++
++    ServerBuilder builder;
++    // Listen on the given address without any authentication mechanism.
++    builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
++    // Register "service" as the instance through which we'll communicate with
++    // clients. In this case it corresponds to an *synchronous* service.
++    builder.RegisterService(this);
++    // Finally assemble the server.
++    server = builder.BuildAndStart();
++    std::cout << "Server listening on " << server_address << std::endl;
++
++    // Wait for the server to shutdown. Note that some other thread must be
++    // responsible for shutting down the server for this call to ever return.
++    server->Wait();
++  }
++
++ private:
++  std::unique_ptr<Server> server;
++};
++
++int main() {
++  GreeterServiceImpl service;
++  service.RunServer();
++
++  return 0;
++}
+diff --git a/tutorial/cpp/Makefile.am b/tutorial/cpp/Makefile.am
+index 184a69d..6f91e28 100755
+--- a/tutorial/cpp/Makefile.am
++++ b/tutorial/cpp/Makefile.am
+@@ -18,44 +18,38 @@
+ #
+ AUTOMAKE_OPTIONS = subdir-objects serial-tests
+ 
+-BUILT_SOURCES = gen-cpp/shared_types.cpp \
+-                gen-cpp/tutorial_types.cpp
++BUILT_SOURCES = gen-cpp/test_types.cpp
+ 
+-noinst_LTLIBRARIES = libtutorialgencpp.la
+-nodist_libtutorialgencpp_la_SOURCES = \
+-	gen-cpp/Calculator.cpp \
+-	gen-cpp/Calculator.h \
+-	gen-cpp/SharedService.cpp \
+-	gen-cpp/SharedService.h \
+-	gen-cpp/shared_constants.cpp \
+-	gen-cpp/shared_constants.h \
+-	gen-cpp/shared_types.cpp \
+-	gen-cpp/shared_types.h \
+-	gen-cpp/tutorial_constants.cpp \
+-	gen-cpp/tutorial_constants.h \
+-	gen-cpp/tutorial_types.cpp \
+-	gen-cpp/tutorial_types.h
++#noinst_LTLIBRARIES = libtutorialgencpp.la
++noinst_LTLIBRARIES = libtestgencpp.la
++nodist_libtestgencpp_la_SOURCES = \
++	gen-cpp/Greeter.grpc.thrift.cpp \
++	gen-cpp/Greeter.grpc.thrift.h \
++	gen-cpp/test_constants.cpp \
++	gen-cpp/test_constants.h \
++	gen-cpp/test_types.cpp \
++	gen-cpp/test_types.h
+ 
+ 
+ 
+-libtutorialgencpp_la_LIBADD = $(top_builddir)/lib/cpp/libthrift.la
++libtestgencpp_la_LIBADD = $(top_builddir)/lib/cpp/libthrift.la
+ 
+ noinst_PROGRAMS = \
+-	TutorialServer \
+-	TutorialClient
++	TestServer \
++	TestClient
+ 
+-TutorialServer_SOURCES = \
+-	CppServer.cpp
++TestServer_SOURCES = \
++	GriftServer.cpp
+ 
+-TutorialServer_LDADD = \
+-	libtutorialgencpp.la \
++TestServer_LDADD = \
++	libtestgencpp.la \
+ 	$(top_builddir)/lib/cpp/libthrift.la
+ 
+-TutorialClient_SOURCES = \
+-	CppClient.cpp
++TestClient_SOURCES = \
++	GriftClient.cpp
+ 
+-TutorialClient_LDADD = \
+-	libtutorialgencpp.la \
++TestClient_LDADD = \
++	libtestgencpp.la \
+ 	$(top_builddir)/lib/cpp/libthrift.la
+ 
+ #
+@@ -63,26 +57,26 @@ TutorialClient_LDADD = \
+ #
+ THRIFT = $(top_builddir)/compiler/cpp/thrift
+ 
+-gen-cpp/Calculator.cpp gen-cpp/SharedService.cpp gen-cpp/shared_constants.cpp gen-cpp/shared_types.cpp gen-cpp/tutorial_constants.cpp gen-cpp/tutorial_types.cpp: $(top_srcdir)/tutorial/tutorial.thrift
++gen-cpp/Greeter.grpc.thrift.cpp gen-cpp/test_constants.cpp gen-cpp/test_types.cpp: $(top_srcdir)/tutorial/cpp/test.thrift
+ 	$(THRIFT) --gen cpp -r $<
+ 
+ AM_CPPFLAGS = $(BOOST_CPPFLAGS) $(LIBEVENT_CPPFLAGS) -I$(top_srcdir)/lib/cpp/src -Igen-cpp
+ AM_CXXFLAGS = -Wall -Wextra -pedantic
+-AM_LDFLAGS = $(BOOST_LDFLAGS) $(LIBEVENT_LDFLAGS)
++AM_LDFLAGS = $(BOOST_LDFLAGS) $(LIBEVENT_LDFLAGS) `pkg-config --libs grpc++ grpc` -lpthread -ldl -lgrpc
+ 
+ clean-local:
+-	$(RM) gen-cpp/*
++	$(RM) -r gen-cpp
+ 
+-tutorialserver: all
+-	./TutorialServer
++testserver: all
++	./TestServer
+ 
+-tutorialclient: all
+-	./TutorialClient
++testclient: all
++	./TestClient
+ 
+ style-local:
+ 	$(CPPSTYLE_CMD)
+ 
+ EXTRA_DIST = \
+ 	CMakeLists.txt \
+-	CppClient.cpp \
+-	CppServer.cpp
++	GriftClient.cpp \
++	GriftServer.cpp
+diff --git a/tutorial/cpp/test.thrift b/tutorial/cpp/test.thrift
+new file mode 100644
+index 0000000..de3c9a4
+--- /dev/null
++++ b/tutorial/cpp/test.thrift
+@@ -0,0 +1,13 @@
++namespace cpp test
++
++struct HelloRequest {
++	1:string name
++}
++
++struct HelloResponse {
++	1:string message
++}
++
++service Greeter {
++	HelloResponse SayHello(1:HelloRequest request);
++}
+\ No newline at end of file
+-- 
+2.8.0.rc3.226.g39d4020
+
+
+From 3e4d75a2e2c474ee7700e7c9acaf89fdb768bedc Mon Sep 17 00:00:00 2001
+From: chedeti <chedeti@google.com>
+Date: Sun, 31 Jul 2016 16:23:53 -0700
+Subject: [PATCH 3/3] grpc java plugins generator
+
+for examples refer to https://github.com/grpc/grpc-java/tree/master/examples/thrift
+---
+ compiler/cpp/src/generate/t_java_generator.cc | 906 +++++++++++++++++++++++++-
+ tutorial/Makefile.am                          |   8 +-
+ 2 files changed, 887 insertions(+), 27 deletions(-)
+
+diff --git a/compiler/cpp/src/generate/t_java_generator.cc b/compiler/cpp/src/generate/t_java_generator.cc
+index 2db8cb8..8b28fe2 100644
+--- a/compiler/cpp/src/generate/t_java_generator.cc
++++ b/compiler/cpp/src/generate/t_java_generator.cc
+@@ -97,10 +97,10 @@ public:
+         } else if(iter->second.compare("suppress") == 0) {
+           suppress_generated_annotations_ = true;
+         } else {
+-          throw "unknown option java:" + iter->first + "=" + iter->second; 
++          throw "unknown option java:" + iter->first + "=" + iter->second;
+         }
+       } else {
+-        throw "unknown option java:" + iter->first; 
++        throw "unknown option java:" + iter->first;
+       }
+     }
+ 
+@@ -195,6 +195,17 @@ public:
+   void generate_service_async_server(t_service* tservice);
+   void generate_process_function(t_service* tservice, t_function* tfunction);
+   void generate_process_async_function(t_service* tservice, t_function* tfunction);
++  void generate_service_impl_base(t_service* tservice);
++  void generate_method_descriptors(t_service* tservice);
++  void generate_stub(t_service* tservice);
++  void generate_blocking_stub(t_service* tservice);
++  void generate_future_stub(t_service* tservice);
++  void generate_method_ids(t_service* tservice);
++  void generate_method_handlers(t_service* tservice);
++  void generate_service_descriptors(t_service* tservice);
++  void generate_service_builder(t_service* tservice);
++  void generate_arg_ids(t_service* tservice);
++  void generate_message_factory(t_service* tservice);
+ 
+   void generate_java_union(t_struct* tstruct);
+   void generate_union_constructor(ofstream& out, t_struct* tstruct);
+@@ -307,6 +318,8 @@ public:
+   std::string java_package();
+   std::string java_type_imports();
+   std::string java_suppressions();
++  std::string grpc_imports();
++  std::string import_extended_service(t_service* tservice);
+   std::string type_name(t_type* ttype,
+                         bool in_container = false,
+                         bool in_init = false,
+@@ -368,7 +381,7 @@ private:
+   bool use_option_type_;
+   bool undated_generated_annotations_;
+   bool suppress_generated_annotations_;
+-  
++
+ };
+ 
+ /**
+@@ -456,6 +469,35 @@ string t_java_generator::java_suppressions() {
+   return "@SuppressWarnings({\"cast\", \"rawtypes\", \"serial\", \"unchecked\", \"unused\"})\n";
+ }
+ 
++string t_java_generator::grpc_imports() {
++  return
++    string() +
++    "import static io.grpc.stub.ClientCalls.asyncUnaryCall;\n" +
++    "import static io.grpc.stub.ClientCalls.asyncServerStreamingCall;\n" +
++    "import static io.grpc.stub.ClientCalls.asyncClientStreamingCall;\n" +
++    "import static io.grpc.stub.ClientCalls.asyncBidiStreamingCall;\n" +
++    "import static io.grpc.stub.ClientCalls.blockingUnaryCall;\n" +
++    "import static io.grpc.stub.ClientCalls.blockingServerStreamingCall;\n" +
++    "import static io.grpc.stub.ClientCalls.futureUnaryCall;\n" +
++    "import static io.grpc.MethodDescriptor.generateFullMethodName;\n" +
++    "import static io.grpc.stub.ServerCalls.asyncUnaryCall;\n" +
++    "import static io.grpc.stub.ServerCalls.asyncServerStreamingCall;\n" +
++    "import static io.grpc.stub.ServerCalls.asyncClientStreamingCall;\n" +
++    "import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall;\n" +
++    "import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;\n" +
++    "import static io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall;\n" +
++    "import io.grpc.thrift.ThriftUtils;\n\n";
++}
++
++string t_java_generator::import_extended_service(t_service* tservice) {
++  if (!tservice) {
++    return string() + "\n";
++  }
++  string ns = tservice->get_program()->get_namespace("java");
++  string extend_service_name = tservice->get_name() + "Grpc";
++  return string() + "import " + ns + "." + extend_service_name + ";\n\n";
++}
++
+ /**
+  * Nothing in Java
+  */
+@@ -2772,25 +2814,51 @@ void t_java_generator::generate_field_value_meta_data(std::ofstream& out, t_type
+  */
+ void t_java_generator::generate_service(t_service* tservice) {
+   // Make output file
+-  string f_service_name = package_dir_ + "/" + make_valid_java_filename(service_name_) + ".java";
++  string f_service_name = package_dir_ + "/" + make_valid_java_filename(service_name_) + "Grpc.java";
+   f_service_.open(f_service_name.c_str());
+ 
+-  f_service_ << autogen_comment() << java_package() << java_type_imports() << java_suppressions();
++  f_service_ <<
++      autogen_comment() <<
++      java_package() <<
++      java_type_imports() <<
++      grpc_imports() <<
++      import_extended_service(tservice->get_extends());
++      java_suppressions();
++
++  f_service_ <<
++    "public class " << service_name_ << "Grpc {" << endl <<
++    endl;
+ 
+-  if (!suppress_generated_annotations_) {
+-    generate_javax_generated_annotation(f_service_);
+-  }
+-  f_service_ << "public class " << service_name_ << " {" << endl << endl;
+   indent_up();
+ 
++  // generate constructor
++  f_service_ <<
++    indent() << "private " << service_name_ <<
++    "Grpc() {}" << endl << endl;
++
++  f_service_ <<
++    indent() << "public static final String SERVICE_NAME = " <<
++    "\"" << package_name_ << "." << service_name_ << "\";" << endl << endl;
++
+   // Generate the three main parts of the service
+   generate_service_interface(tservice);
+-  generate_service_async_interface(tservice);
+-  generate_service_client(tservice);
+-  generate_service_async_client(tservice);
+-  generate_service_server(tservice);
+-  generate_service_async_server(tservice);
++  generate_arg_ids(tservice);
++  generate_message_factory(tservice);
++  generate_service_impl_base(tservice);
++  //generate_service_async_interface(tservice);
++  //generate_service_client(tservice);
++  //generate_service_async_client(tservice);
++  //generate_service_server(tservice);
++  //generate_service_async_server(tservice);
+   generate_service_helpers(tservice);
++  generate_method_descriptors(tservice);
++  generate_stub(tservice);
++  generate_blocking_stub(tservice);
++  generate_future_stub(tservice);
++  generate_method_ids(tservice);
++  generate_method_handlers(tservice);
++  generate_service_descriptors(tservice);
++  generate_service_builder(tservice);
+ 
+   indent_down();
+   f_service_ << "}" << endl;
+@@ -2805,24 +2873,820 @@ void t_java_generator::generate_service(t_service* tservice) {
+ void t_java_generator::generate_service_interface(t_service* tservice) {
+   string extends = "";
+   string extends_iface = "";
+-  if (tservice->get_extends() != NULL) {
+-    extends = type_name(tservice->get_extends());
+-    extends_iface = " extends " + extends + ".Iface";
+-  }
+ 
+   generate_java_doc(f_service_, tservice);
+-  f_service_ << indent() << "public interface Iface" << extends_iface << " {" << endl << endl;
++  f_service_ << indent() <<
++    "@java.lang.Deprecated public static interface " << service_name_;
++
++  if (tservice->get_extends()) {
++    f_service_ << " extends " << tservice->get_extends()->get_name() + "Grpc." <<
++      tservice->get_extends()->get_name() << endl;
++  }
++  f_service_ << " {" << endl;
++
++  indent_up();
++  vector<t_function*> functions = tservice->get_functions();
++  vector<t_function*>::iterator f_iter;
++  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    //generate_java_doc(f_service_, *f_iter);
++    f_service_ <<
++      indent() << "public void " << (*f_iter)->get_name() << "(" << (*f_iter)->get_name() <<
++      "_args request," << endl <<
++      indent() << "    io.grpc.stub.StreamObserver<" << (*f_iter)->get_name() <<
++      "_result> responseObserver);" << endl << endl;
++  }
++  indent_down();
++  f_service_ << indent() << "}" << endl << endl;
++}
++
++void t_java_generator::generate_arg_ids(t_service* tservice) {
++  vector<t_function*> functions = tservice->get_functions();
++  vector<t_function*>::iterator f_iter;
++  int i=0;
++  for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    f_service_ << indent() <<
++      "private static final int ARG_IN_METHOD_" <<
++      (*f_iter)->get_name() << " = " << ++i << ";" << endl;
++    f_service_ << indent() <<
++      "private static final int ARG_OUT_METHOD_" <<
++      (*f_iter)->get_name() << " = " << ++i << ";" << endl;
++  }
++  f_service_ << endl;
++
++  if (tservice->get_extends()) {
++    f_service_ << indent() << "// ARG IDs for extended service" << endl;
++    t_service* extend_service = tservice->get_extends();
++    functions = extend_service->get_functions();
++    for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      f_service_ << indent() <<
++        "private static final int ARG_IN_METHOD_" <<
++        (*f_iter)->get_name() << " = " << ++i << ";" << endl;
++      f_service_ << indent() <<
++        "private static final int ARG_OUT_METHOD_" <<
++        (*f_iter)->get_name() << " = " << ++i << ";" << endl;
++    }
++    f_service_ << endl;
++  }
++}
++
++void t_java_generator::generate_message_factory(t_service* tservice) {
++  f_service_ << indent() <<
++    "private static final class ThriftMessageFactory<T extends " <<
++    "org.apache.thrift.TBase<T,?>>" << endl << indent() <<
++    "    implements io.grpc.thrift.MessageFactory<T> {" << endl;
++  indent_up();
++  f_service_ << indent() <<
++      "private final int id;" << endl << endl;
++  f_service_ << endl;
++
++  f_service_ << indent() <<
++    "ThriftMessageFactory(int id) {" << endl <<
++    indent() << "  this.id = id;" << endl <<
++    indent() << "}" << endl;
++
++  f_service_ << indent() <<
++    "@java.lang.Override" << endl <<
++    indent() << "public T newInstance() {" << endl;
++  indent_up();
++
++  f_service_ << indent() <<
++    "Object o;" << endl <<
++    indent() << "switch (id) {" << endl;
++
++  vector<t_function*> functions = tservice->get_functions();
++  vector<t_function*>::iterator f_iter;
++  for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    f_service_ << indent() <<
++      "case ARG_IN_METHOD_" << (*f_iter)->get_name() << ":" << endl <<
++      indent() << "  o = new " << (*f_iter)->get_name() << "_args();" <<
++      endl << indent() << "  break;" << endl;
++    f_service_ << indent() <<
++      "case ARG_OUT_METHOD_" << (*f_iter)->get_name() << ":" << endl <<
++      indent() << " o = new " << (*f_iter)->get_name() << "_result();" <<
++      endl << indent() << "  break;" << endl;
++  }
++
++  if (tservice->get_extends()) {
++    t_service* extend_service = tservice->get_extends();
++    functions = extend_service->get_functions();
++    string extend_service_name = extend_service->get_name() + "Grpc";
++    for (f_iter = functions.begin(); f_iter!= functions.end(); ++f_iter) {
++      f_service_ << indent() <<
++        "case ARG_IN_METHOD_" << (*f_iter)->get_name() << ":" << endl <<
++        indent() << "  o = new " << extend_service_name << "." << (*f_iter)->get_name() << "_args();" <<
++        endl << indent() << "  break;" << endl;
++      f_service_ << indent() <<
++        "case ARG_OUT_METHOD_" << (*f_iter)->get_name() << ":" << endl <<
++        indent() << " o = new " << extend_service_name << "." << (*f_iter)->get_name() << "_result();" <<
++        endl << indent() << "  break;" << endl;
++    }
++  }
++
++  f_service_  << indent() <<
++    "default:" << endl << indent() <<
++    "  throw new AssertionError();" << endl << indent() <<
++    "}" << endl;
++
++  f_service_ << indent() <<
++    "@java.lang.SuppressWarnings(\"unchecked\")" << endl <<
++    indent() << "T t = (T) o;" << endl << indent() <<
++    "return t;" << endl;
++
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl;
++
++  indent_down();
++  f_service_ << indent() << "}" << endl;
++}
++
++void t_java_generator::generate_service_impl_base(t_service* tservice) {
++  f_service_ <<
++    indent() << "public static abstract class " << service_name_ <<
++    "ImplBase implements " << service_name_ << ", io.grpc.BindableService {" << endl;
++  indent_up();
++
++  vector<t_function*> functions = tservice->get_functions();
++  vector<t_function*>::iterator f_iter;
++  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    f_service_ <<
++      indent() << "@java.lang.Override" << endl <<
++      indent() << "public void " << (*f_iter)->get_name() << "(" << (*f_iter)->get_name() <<
++      "_args request, " << endl <<
++      indent() << "    io.grpc.stub.StreamObserver<" << (*f_iter)->get_name() <<
++      "_result> responseObserver) {" << endl;
++    indent_up();
++    f_service_ <<
++      indent() << "asyncUnimplementedUnaryCall(METHOD_" << (*f_iter)->get_name() <<
++      ", responseObserver);" << endl;
++    indent_down();
++    f_service_ <<
++      indent() << "}" << endl << endl;
++  }
++
++  if (tservice->get_extends()) {
++    t_service* extend_service = tservice->get_extends();
++    functions = extend_service->get_functions();
++    string extend_service_name = extend_service->get_name() + "Grpc" ;
++    for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      f_service_ <<
++        indent() << "@java.lang.Override" << endl <<
++        indent() << "public void " << (*f_iter)->get_name() << "(" <<
++        extend_service_name << "." << (*f_iter)->get_name() <<
++        "_args request, " << endl <<
++        indent() << "    io.grpc.stub.StreamObserver<" << extend_service_name
++        << "." << (*f_iter)->get_name() << "_result> responseObserver) {" << endl;
++      indent_up();
++      f_service_ <<
++        indent() << "asyncUnimplementedUnaryCall(METHOD_" << (*f_iter)->get_name() <<
++        ", responseObserver);" << endl;
++      indent_down();
++      f_service_ <<
++        indent() << "}" << endl << endl;
++    }
++  }
++
++  f_service_ <<
++    indent() << "@java.lang.Override" <<
++    " public io.grpc.ServerServiceDefinition bindService() {" << endl;
+   indent_up();
++  f_service_ <<
++    indent() << "return " << service_name_ << "Grpc.bindService(this);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  // generate Abstract Service
++  f_service_ <<
++    indent() << "@java.lang.Deprecated public static abstract class Abstract" << service_name_ <<
++    " extends " << service_name_ << "ImplBase {}" << endl << endl;
++}
++
++void t_java_generator::generate_method_descriptors(t_service* tservice) {
++  vector<t_function*> functions = tservice->get_functions();
++  vector<t_function*>::iterator f_iter;
++  for( f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    f_service_ <<
++      indent() << "public static final io.grpc.MethodDescriptor<" <<
++      (*f_iter)->get_name() << "_args," << endl <<
++      indent() << "    " << (*f_iter)->get_name() << "_result> METHOD_" << (*f_iter)->get_name() <<
++      " = " << endl << indent() << "    io.grpc.MethodDescriptor.create(" << endl;
++    indent_up();
++    f_service_ <<
++      indent() << "    io.grpc.MethodDescriptor.MethodType.UNARY," << endl <<
++      indent() << "    generateFullMethodName(" << "\"" << package_name_ << "." <<
++      service_name_ << "\" , \"" << (*f_iter)->get_name() << "\")," << endl <<
++      indent() << "    io.grpc.thrift.ThriftUtils.marshaller(" << endl <<
++      indent() << "    new ThriftMessageFactory<" << (*f_iter)->get_name() <<
++      "_args>( ARG_IN_METHOD_" << (*f_iter)->get_name() << "))," << endl <<
++      indent() << "    io.grpc.thrift.ThriftUtils.marshaller(" << endl <<
++      indent() << "    new ThriftMessageFactory<" << (*f_iter)->get_name() <<
++      "_result>( ARG_OUT_METHOD_" << (*f_iter)->get_name() << ")));" << endl << endl;
++    indent_down();
++  }
++
++  if(tservice->get_extends()) {
++    t_service* extends_service = tservice->get_extends();
++    functions = extends_service->get_functions();
++    string extend_service_name = extends_service->get_name() + "Grpc";
++    for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      f_service_ <<
++        indent() << "public static final io.grpc.MethodDescriptor<" << extend_service_name << "." <<
++        (*f_iter)->get_name() << "_args," << endl <<
++        indent() << "    " << extend_service_name << "." << (*f_iter)->get_name() << "_result> METHOD_"
++        << (*f_iter)->get_name() << " = " << endl << indent() <<
++        "    io.grpc.MethodDescriptor.create(" << endl;
++      indent_up();
++      f_service_ <<
++        indent() << "    io.grpc.MethodDescriptor.MethodType.UNARY," << endl <<
++        indent() << "    generateFullMethodName(" << "\"" << package_name_ << "." <<
++        service_name_ << "\" , \"" << (*f_iter)->get_name() << "\")," << endl <<
++        indent() << "    io.grpc.thrift.ThriftUtils.marshaller(" << endl <<
++        indent() << "    new ThriftMessageFactory<" << extend_service_name << "." <<
++        (*f_iter)->get_name() << "_args>( ARG_IN_METHOD_" << (*f_iter)->get_name() << "))," << endl <<
++        indent() << "    io.grpc.thrift.ThriftUtils.marshaller(" << endl <<
++        indent() << "    new ThriftMessageFactory<" << extend_service_name << "." << (*f_iter)->get_name() <<
++        "_result>( ARG_OUT_METHOD_" << (*f_iter)->get_name() << ")));" << endl << endl;
++      indent_down();
++    }
++  }
++}
++
++void t_java_generator::generate_stub(t_service* tservice) {
++  f_service_ <<
++    indent() <<
++    "public static " << service_name_ <<
++    "Stub newStub(io.grpc.Channel channel) {" <<
++    endl;
++
++  indent_up();
++  f_service_ <<
++    indent() <<
++    "return new " << service_name_ << "Stub(channel);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  // generate Stub impl
++
++  f_service_ <<
++    indent() << "public static class " <<
++    service_name_ << "Stub extends io.grpc.stub.AbstractStub<" <<
++    service_name_ << "Stub>" << endl <<
++    indent() << "    implements " << service_name_ << "{" << endl;
++  indent_up();
++
++  f_service_ <<
++    indent() << "private " << service_name_ << "Stub(io.grpc.Channel channel) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "super(channel);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  f_service_ <<
++    indent() << "private " << service_name_ << "Stub(io.grpc.Channel channel, " << endl <<
++    indent() << "    io.grpc.CallOptions callOptions) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "super(channel, callOptions);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  f_service_ <<
++    indent() << "@java.lang.Override" << endl <<
++    indent() << "protected " << service_name_ << "Stub build(io.grpc.Channel channel, " <<
++    endl << indent() << "    io.grpc.CallOptions callOptions) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "return new " << service_name_ << "Stub(channel, callOptions);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  vector<t_function*> functions = tservice->get_functions();
++  vector<t_function*>::iterator f_iter;
++  for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    f_service_ <<
++      indent() << "@java.lang.Override" << endl <<
++      indent() << "public void " << (*f_iter)->get_name() << "(" <<
++      (*f_iter)->get_name() << "_args request," << endl << indent() <<
++      "    io.grpc.stub.StreamObserver<" << (*f_iter)->get_name() <<
++      "_result> responseObserver) {" << endl;
++    indent_up();
++    f_service_ <<
++      indent() << "asyncUnaryCall(" << endl <<
++      indent() << "    getChannel().newCall(METHOD_" << (*f_iter)->get_name() <<
++      ", getCallOptions()), request, responseObserver);" << endl;
++    indent_down();
++    f_service_ <<
++      indent() << "}" << endl << endl;
++  }
++
++  if (tservice->get_extends()) {
++    t_service* extend_service = tservice->get_extends();
++    functions = extend_service->get_functions();
++    string extend_service_name = extend_service->get_name() + "Grpc";
++    for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      f_service_ <<
++        indent() << "@java.lang.Override" << endl <<
++        indent() << "public void " << (*f_iter)->get_name() << "(" <<
++        extend_service_name << "." << (*f_iter)->get_name() << "_args request,"
++        << endl << indent() << "    io.grpc.stub.StreamObserver<" <<
++        extend_service_name << "." << (*f_iter)->get_name() <<
++        "_result> responseObserver) {" << endl;
++      indent_up();
++      f_service_ <<
++        indent() << "asyncUnaryCall(" << endl <<
++        indent() << "    getChannel().newCall(METHOD_" << (*f_iter)->get_name() <<
++        ", getCallOptions()), request, responseObserver);" << endl;
++      indent_down();
++      f_service_ <<
++        indent() << "}" << endl << endl;
++    }
++  }
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++}
++
++void t_java_generator::generate_blocking_stub(t_service* tservice) {
++  f_service_ <<
++    indent() << "public static " << service_name_ <<
++    "BlockingStub newBlockingStub(" << endl <<
++    indent() << "    io.grpc.Channel channel) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "return new " << service_name_ << "BlockingStub(channel);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  // generate Blocking Client
++  f_service_ <<
++    indent() << "@java.lang.Deprecated public static interface " << service_name_ <<
++    "BlockingClient " ;
++  
++  if (tservice->get_extends()) {
++    string extend_service_name = tservice->get_extends()->get_name();
++    f_service_ << endl << indent() << "    extends " << extend_service_name << "Grpc." <<
++        extend_service_name << "BlockingClient " ;
++  }
++
++  f_service_ << "{" << endl;
++
++  indent_up();
++
+   vector<t_function*> functions = tservice->get_functions();
+   vector<t_function*>::iterator f_iter;
+   for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+-    generate_java_doc(f_service_, *f_iter);
+-    indent(f_service_) << "public " << function_signature(*f_iter) << ";" << endl << endl;
++    f_service_ <<
++      indent() << "public " << (*f_iter)->get_name() << "_result " <<
++      (*f_iter)->get_name() << "(" << (*f_iter)->get_name() << "_args request);" << endl << endl;
++  }
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  // generate Blocking Stub impl
++
++  f_service_ <<
++    indent() << "public static class " <<
++    service_name_ << "BlockingStub extends io.grpc.stub.AbstractStub<" <<
++    service_name_ << "BlockingStub>" << endl <<
++    indent() << "    implements " << service_name_ << "BlockingClient {";
++
++  indent_up();
++
++  f_service_ <<
++    indent() << "private " << service_name_ << "BlockingStub(io.grpc.Channel channel) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "super(channel);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  f_service_ <<
++    indent() << "private " << service_name_ << "BlockingStub(io.grpc.Channel channel, " << endl <<
++    indent() << "    io.grpc.CallOptions callOptions) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "super(channel, callOptions);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  f_service_ <<
++    indent() << "@java.lang.Override" << endl <<
++    indent() << "protected " << service_name_ << "BlockingStub build(io.grpc.Channel channel, " <<
++    endl << indent() << "    io.grpc.CallOptions callOptions) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "return new " << service_name_ << "BlockingStub(channel, callOptions);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    f_service_ <<
++      indent() << "@java.lang.Override" << endl <<
++      indent() << "public " << (*f_iter)->get_name() << "_result " << (*f_iter)->get_name() << "(" <<
++      (*f_iter)->get_name() << "_args request) {" << endl;
++    indent_up();
++    f_service_ <<
++      indent() << "return blockingUnaryCall(" << endl <<
++      indent() << "    getChannel(), METHOD_" << (*f_iter)->get_name() <<
++      ", getCallOptions(), request);" << endl;
++    indent_down();
++    f_service_ <<
++      indent() << "}" << endl << endl;
++  }
++
++  if (tservice->get_extends()) {
++    t_service* extend_service = tservice->get_extends();
++    functions = extend_service->get_functions();
++    string extend_service_name = extend_service->get_name() + "Grpc";
++    for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      f_service_ <<
++        indent() << "@java.lang.Override" << endl <<
++        indent() << "public " << extend_service_name << "." << (*f_iter)->get_name() <<
++        "_result " << (*f_iter)->get_name() << "(" << extend_service_name << "." <<
++        (*f_iter)->get_name() << "_args request) {" << endl;
++      indent_up();
++      f_service_ <<
++        indent() << "return blockingUnaryCall(" << endl <<
++        indent() << "    getChannel(), METHOD_" << (*f_iter)->get_name() <<
++        ", getCallOptions(), request);" << endl;
++      indent_down();
++      f_service_ <<
++        indent() << "}" << endl << endl;
++    }
++  }
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++}
++
++void t_java_generator::generate_future_stub(t_service* tservice) {
++  f_service_ <<
++    indent() << "public static " << service_name_ <<
++    "FutureStub newFutureStub(" << endl <<
++    indent() << "    io.grpc.Channel channel) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "return new " << service_name_ << "FutureStub(channel);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  // generate Future Client
++  f_service_ <<
++    indent() << "@java.lang.Deprecated public static interface " << service_name_ <<
++    "FutureClient " ;
++
++  if (tservice->get_extends()) {
++    string extend_service_name = tservice->get_extends()->get_name();
++    f_service_ << endl << indent() << "    extends " << extend_service_name << "Grpc." <<
++        extend_service_name << "FutureClient " ; 
++  }
++  f_service_ << "{" << endl;
++
++  indent_up();
++
++  vector<t_function*> functions = tservice->get_functions();
++  vector<t_function*>::iterator f_iter;
++  for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    f_service_ <<
++      indent() << "public com.google.common.util.concurrent.ListenableFuture<" <<
++      (*f_iter)->get_name() << "_result> " << (*f_iter)->get_name() << "(" << endl <<
++      indent() << "    " << (*f_iter)->get_name() << "_args request);" << endl << endl;
++  }
++
++  if (tservice->get_extends()) {
++    t_service* extend_service = tservice->get_extends();
++    functions = extend_service->get_functions();
++    string extend_service_name = extend_service->get_name() + "Grpc";
++    for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      f_service_ <<
++        indent() << "public com.google.common.util.concurrent.ListenableFuture<" <<
++        extend_service_name << "." << (*f_iter)->get_name() << "_result> " <<
++        (*f_iter)->get_name() << "(" << endl <<
++        indent() << "    " << extend_service_name << "." << (*f_iter)->get_name() <<
++        "_args request);" << endl << endl;
++    }
++  }
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  // generate Stub impl
++
++  f_service_ <<
++    indent() << "public static class " <<
++    service_name_ << "FutureStub extends io.grpc.stub.AbstractStub<" <<
++    service_name_ << "FutureStub>" << endl <<
++    indent() << "    implements " << service_name_ << "FutureClient {" << endl;
++  indent_up();
++
++  f_service_ <<
++    indent() << "private " << service_name_ << "FutureStub(io.grpc.Channel channel) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "super(channel);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  f_service_ <<
++    indent() << "private " << service_name_ << "FutureStub(io.grpc.Channel channel, " << endl <<
++    indent() << "    io.grpc.CallOptions callOptions) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "super(channel, callOptions);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  f_service_ <<
++    indent() << "@java.lang.Override" << endl <<
++    indent() << "protected " << service_name_ << "FutureStub build(io.grpc.Channel channel, " <<
++    endl << indent() << "    io.grpc.CallOptions callOptions) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "return new " << service_name_ << "FutureStub(channel, callOptions);" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  functions = tservice->get_functions();
++  for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    f_service_ <<
++      indent() << "@java.lang.Override" << endl <<
++      indent() << "public com.google.common.util.concurrent.ListenableFuture<" <<
++      (*f_iter)->get_name() << "_result> " << (*f_iter)->get_name() << "(" <<
++      endl << indent() << "    " << (*f_iter)->get_name() << "_args request) {" << endl;
++    indent_up();
++    f_service_ <<
++      indent() << "return futureUnaryCall(" << endl <<
++      indent() << "    getChannel().newCall(METHOD_" << (*f_iter)->get_name() <<
++      ", getCallOptions()), request);" << endl;
++    indent_down();
++    f_service_ <<
++      indent() << "}" << endl << endl;
++  }
++
++  if (tservice->get_extends()) {
++    t_service* extend_service = tservice->get_extends();
++    functions = extend_service->get_functions();
++    string extend_service_name = extend_service->get_name() + "Grpc";
++    for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      f_service_ <<
++        indent() << "@java.lang.Override" << endl <<
++        indent() << "public com.google.common.util.concurrent.ListenableFuture<" <<
++        extend_service_name << "." << (*f_iter)->get_name() << "_result> " <<
++        (*f_iter)->get_name() << "(" << endl << indent() << "    " <<
++        extend_service_name << "." << (*f_iter)->get_name() << "_args request) {" << endl;
++      indent_up();
++      f_service_ <<
++        indent() << "return futureUnaryCall(" << endl <<
++        indent() << "    getChannel().newCall(METHOD_" << (*f_iter)->get_name() <<
++        ", getCallOptions()), request);" << endl;
++      indent_down();
++      f_service_ <<
++        indent() << "}" << endl << endl;
++    }
++  }
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++}
++
++void t_java_generator::generate_method_ids(t_service* tservice) {
++  vector<t_function*> functions = tservice->get_functions();
++  vector<t_function*>::iterator f_iter;
++  int i=0;
++  for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter, ++i) {
++    f_service_ <<
++      indent() << "private static final int METHODID_" <<
++      (*f_iter)->get_name() << " = " << i << ";" << endl;
++  }
++  if (tservice->get_extends()) {
++    t_service* extend_service = tservice->get_extends();
++    functions = extend_service->get_functions();
++    string extend_service_name = extend_service->get_name() + "Grpc";
++    for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter, ++i) {
++      f_service_ <<
++        indent() << "private static final int METHODID_" <<
++        (*f_iter)->get_name() << " = " << i << ";" << endl;
++    }
++  }
++  f_service_ << endl;
++}
++
++void t_java_generator::generate_method_handlers(t_service* tservice) {
++  f_service_ <<
++    indent() << "private static class MethodHandlers<Req, Resp> implements" <<
++    endl << indent() << "    io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>," <<
++    endl << indent() << "    io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>," <<
++    endl << indent() << "    io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>," <<
++    endl << indent() << "    io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {" <<
++    endl;
++  indent_up();
++  f_service_ <<
++    indent() << "private final " << service_name_ << " serviceImpl;" << endl <<
++    indent() << "private final int methodId;" << endl << endl;
++
++  f_service_ <<
++    indent() << "public MethodHandlers(" << service_name_ << " serviceImpl, int " <<
++    "methodId) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "this.serviceImpl = serviceImpl;" << endl <<
++    indent() << "this.methodId = methodId;" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  // invoke
++  f_service_ <<
++    indent() << "@java.lang.Override" << endl <<
++    indent() << "@java.lang.SuppressWarnings(\"unchecked\")" << endl <<
++    indent() << "public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserver) {" <<
++    endl;
++  indent_up();
++  f_service_ <<
++    indent() << "switch (methodId) {" << endl;
++  indent_up();
++
++  vector<t_function*> functions = tservice->get_functions();
++  vector<t_function*>::iterator f_iter;
++  for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    f_service_ <<
++      indent() << "case METHODID_" << (*f_iter)->get_name() << ":" << endl;
++    indent_up();
++    f_service_ <<
++      indent() << "serviceImpl." << (*f_iter)->get_name() << "((" << (*f_iter)->get_name() <<
++      "_args) request," << endl <<
++      indent() << "    (io.grpc.stub.StreamObserver<" << (*f_iter)->get_name() << "_result>)" <<
++      " responseObserver);" << endl <<
++      indent() << "break;" << endl << endl;
++    indent_down();
++  }
++  if (tservice->get_extends()) {
++    t_service* extend_service = tservice->get_extends();
++    functions = extend_service->get_functions();
++    string extend_service_name = extend_service->get_name() + "Grpc";
++    for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      f_service_ <<
++        indent() << "case METHODID_" << (*f_iter)->get_name() << ":" << endl;
++      indent_up();
++      f_service_ <<
++        indent() << "serviceImpl." << (*f_iter)->get_name() << "((" << extend_service_name <<
++        "." << (*f_iter)->get_name() << "_args) request," << endl <<
++        indent() << "    (io.grpc.stub.StreamObserver<" << extend_service_name << "." <<
++        (*f_iter)->get_name() << "_result>)" << " responseObserver);" << endl <<
++        indent() << "break;" << endl << endl;
++      indent_down();
++    }
+   }
++  f_service_ <<
++    indent() << "default:" << endl <<
++    indent() << "  throw new AssertionError();" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl;
++  indent_down();
++  f_service_ <<
++    indent() << "}" << endl << endl;
++
++  // invoke
++  f_service_ <<
++    indent() << "@java.lang.Override" << endl <<
++    indent() << "@java.lang.SuppressWarnings(\"unchecked\")" << endl <<
++    indent() << "public io.grpc.stub.StreamObserver<Req> invoke(" << endl <<
++    indent() << "    io.grpc.stub.StreamObserver<Resp> responseObserver) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "switch (methodId) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "default:" << endl;
++  f_service_ <<
++    indent() << "  throw new AssertionError();" << endl;
++  indent_down();
++  f_service_ << indent() << "}" << endl;
+   indent_down();
+   f_service_ << indent() << "}" << endl << endl;
++  indent_down();
++  f_service_ << indent() << "}" << endl << endl;
++
+ }
+ 
++void t_java_generator::generate_service_descriptors(t_service* tservice) {
++  // generate service descriptor
++  vector<t_function*> functions = tservice->get_functions();
++  vector<t_function*>::iterator f_iter;
++  f_service_ <<
++    indent() << "public static io.grpc.ServiceDescriptor getServiceDescriptor() {" <<
++    endl;
++  indent_up();
++  f_service_ <<
++    indent() << "return new io.grpc.ServiceDescriptor(SERVICE_NAME";
++  for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    f_service_ <<
++      indent() << "," << endl <<
++      indent() << "    METHOD_" << (*f_iter)->get_name();
++  }
++  if (tservice->get_extends()) {
++    t_service* extend_service = tservice->get_extends();
++    functions = extend_service->get_functions();
++    for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      f_service_ <<
++        indent() << "," << endl <<
++        indent() << "    METHOD_" << (*f_iter)->get_name();
++    }
++  }
++  f_service_ << ");" << endl;
++  indent_down();
++  f_service_ << indent() << "}" << endl << endl;
++}
++
++void t_java_generator::generate_service_builder(t_service* tservice) {
++  // bind Service
++  vector<t_function*> functions = tservice->get_functions();
++  vector<t_function*>::iterator f_iter;
++  f_service_ <<
++    indent() << "@java.lang.Deprecated public static io.grpc.ServerServiceDefinition" <<
++    " bindService(" << endl <<
++    indent() << "    final " << service_name_ << " serviceImpl) {" << endl;
++  indent_up();
++  f_service_ <<
++    indent() << "return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())" <<
++    endl;
++  for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++    f_service_ <<
++      indent() << "    .addMethod(" << endl;
++    indent_up();
++    f_service_ <<
++      indent() << "    METHOD_" << (*f_iter)->get_name() << "," << endl <<
++      indent() << "    asyncUnaryCall(" << endl;
++    indent_up();
++    f_service_ <<
++      indent() << "    new MethodHandlers<" << endl;
++    indent_up();
++    f_service_ <<
++      indent() << "    " << (*f_iter)->get_name() << "_args," << endl <<
++      indent() << "    " << (*f_iter)->get_name() << "_result>(" << endl;
++    indent_up();
++    f_service_ <<
++      indent() << "    serviceImpl, METHODID_" << (*f_iter)->get_name() << ")))" << endl;
++    indent_down();
++    indent_down();
++    indent_down();
++    indent_down();
++  }
++
++  if (tservice->get_extends()) {
++    t_service* extend_service = tservice->get_extends();
++    functions = extend_service->get_functions();
++    string extend_service_name = extend_service->get_name() + "Grpc";
++    for(f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
++      f_service_ <<
++        indent() << "    .addMethod(" << endl;
++      indent_up();
++      f_service_ <<
++        indent() << "    METHOD_" << (*f_iter)->get_name() << "," << endl <<
++        indent() << "    asyncUnaryCall(" << endl;
++      indent_up();
++      f_service_ <<
++        indent() << "    new MethodHandlers<" << endl;
++      indent_up();
++      f_service_ <<
++        indent() << "    " << extend_service_name << "." << (*f_iter)->get_name() << "_args," << endl <<
++        indent() << "    " << extend_service_name << "." << (*f_iter)->get_name() << "_result>(" << endl;
++      indent_up();
++      f_service_ <<
++        indent() << "    serviceImpl, METHODID_" << (*f_iter)->get_name() << ")))" << endl;
++      indent_down();
++      indent_down();
++      indent_down();
++      indent_down();
++    }
++  }
++  f_service_ <<
++    indent() << "    .build();" << endl;
++  indent_down();
++  f_service_ << indent() << "}" << endl << endl;
++}
++
++
+ void t_java_generator::generate_service_async_interface(t_service* tservice) {
+   string extends = "";
+   string extends_iface = "";
+diff --git a/tutorial/Makefile.am b/tutorial/Makefile.am
+index 5865c54..1cffbe6 100755
+--- a/tutorial/Makefile.am
++++ b/tutorial/Makefile.am
+@@ -35,11 +35,6 @@ if WITH_D
+ SUBDIRS += d
+ endif
+ 
+-if WITH_JAVA
+-SUBDIRS += java
+-SUBDIRS += js
+-endif
+-
+ if WITH_PYTHON
+ SUBDIRS += py
+ SUBDIRS += py.twisted
+@@ -95,4 +90,5 @@ EXTRA_DIST = \
+ 	php \
+ 	shared.thrift \
+ 	tutorial.thrift \
+-	README.md
++	README.md \
++	java
+-- 
+2.8.0.rc3.226.g39d4020
+
diff --git a/tools/jenkins/run_full_cloud_prod.sh b/tools/jenkins/run_full_cloud_prod.sh
new file mode 100755
index 0000000..ea51b50
--- /dev/null
+++ b/tools/jenkins/run_full_cloud_prod.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+# Copyright 2016, 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.
+#
+# This script is invoked by Jenkins and runs interop test suite.
+set -ex
+
+export LANG=en_US.UTF-8
+
+# Enter the gRPC repo root
+cd $(dirname $0)/../..
+
+tools/run_tests/run_interop_tests.py \
+    -l all \
+    --cloud_to_prod \
+    --cloud_to_prod_auth \
+    --prod_servers default cloud_gateway gateway_v4 cloud_gateway_v4 \
+    --use_docker -t -j 12 $@ || true
diff --git a/tools/jenkins/run_full_interop.sh b/tools/jenkins/run_full_interop.sh
deleted file mode 100755
index a82da1c..0000000
--- a/tools/jenkins/run_full_interop.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2016, 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.
-#
-# This script is invoked by Jenkins and runs interop test suite.
-set -ex
-
-# Enter the gRPC repo root
-cd $(dirname $0)/../..
-
-tools/run_tests/run_interop_tests.py -l all -s all --cloud_to_prod --cloud_to_prod_auth --prod_servers default cloud_gateway gateway_v2 cloud_gateway_v2 gateway_v4 cloud_gateway_v4 --use_docker --http2_interop -t -j 12 $@ || true
diff --git a/tools/jenkins/run_full_performance.sh b/tools/jenkins/run_full_performance.sh
index 3feda86..7631db8 100755
--- a/tools/jenkins/run_full_performance.sh
+++ b/tools/jenkins/run_full_performance.sh
@@ -38,7 +38,7 @@
 tools/run_tests/run_performance_tests.py \
     -l c++ csharp node ruby java python go \
     --netperf \
-    --category all \
+    --category scalable \
     --bq_result_table performance_test.performance_experiment \
     --remote_worker_host grpc-performance-server-8core grpc-performance-client-8core grpc-performance-client2-8core \
     || EXIT_CODE=1
@@ -53,4 +53,3 @@
     || EXIT_CODE=1
 
 exit $EXIT_CODE
-
diff --git a/tools/jenkins/run_interop.sh b/tools/jenkins/run_interop.sh
index a424aea..4a7bff3 100755
--- a/tools/jenkins/run_interop.sh
+++ b/tools/jenkins/run_interop.sh
@@ -31,6 +31,8 @@
 # This script is invoked by Jenkins and runs interop test suite.
 set -ex
 
+export LANG=en_US.UTF-8
+
 # Enter the gRPC repo root
 cd $(dirname $0)/../..
 
diff --git a/tools/jenkins/run_jenkins_matrix.sh b/tools/jenkins/run_jenkins_matrix.sh
new file mode 100755
index 0000000..b3783e6
--- /dev/null
+++ b/tools/jenkins/run_jenkins_matrix.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# This script is invoked by Jenkins and triggers a test run, bypassing
+# all args to the test script.
+#
+# Setting up rvm environment BEFORE we set -ex.
+[[ -s /etc/profile.d/rvm.sh ]] && . /etc/profile.d/rvm.sh
+# To prevent cygwin bash complaining about empty lines ending with \r
+# we set the igncr option. The option doesn't exist on Linux, so we fallback
+# to just 'set -ex' there.
+# NOTE: No empty lines should appear in this file before igncr is set!
+set -ex -o igncr || set -ex
+
+python tools/run_tests/run_tests_matrix.py $@
diff --git a/tools/jenkins/run_sweep_performance.sh b/tools/jenkins/run_sweep_performance.sh
new file mode 100755
index 0000000..6cd7a91
--- /dev/null
+++ b/tools/jenkins/run_sweep_performance.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+# Copyright 2016, 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.
+#
+# This script is invoked by Jenkins and runs full performance test suite.
+set -ex
+
+# Enter the gRPC repo root
+cd $(dirname $0)/../..
+
+# scalability with 32cores (and upload to a different BQ table)
+tools/run_tests/run_performance_tests.py \
+    -l c++ \
+    --category sweep \
+    --bq_result_table performance_test.performance_experiment_32core \
+    --remote_worker_host grpc-performance-server-32core grpc-performance-client-32core grpc-performance-client2-32core \
+    || EXIT_CODE=1
+
+exit $EXIT_CODE
diff --git a/tools/run_tests/artifact_targets.py b/tools/run_tests/artifact_targets.py
index 8550ee7..d36f963 100644
--- a/tools/run_tests/artifact_targets.py
+++ b/tools/run_tests/artifact_targets.py
@@ -31,6 +31,8 @@
 """Definition of targets to build artifacts."""
 
 import os.path
+import random
+import string
 import sys
 
 import jobset
@@ -79,27 +81,16 @@
   'x64': '-m64'
 }
 
-python_windows_version_arch_map = {
-  ('x86', '2.7'): 'Python27_32bits',
-  ('x64', '2.7'): 'Python27',
-  ('x86', '3.4'): 'Python34_32bits',
-  ('x64', '3.4'): 'Python34',
-}
 
 class PythonArtifact:
   """Builds Python artifacts."""
 
-  def __init__(self, platform, arch, python_version, manylinux_build=None):
-    if manylinux_build:
-      self.name = 'python%s_%s_%s_%s' % (python_version, platform, arch, manylinux_build)
-    else:
-      self.name = 'python%s_%s_%s' % (python_version, platform, arch)
+  def __init__(self, platform, arch, py_version):
+    self.name = 'python_%s_%s_%s' % (platform, arch, py_version)
     self.platform = platform
     self.arch = arch
-    self.labels = ['artifact', 'python', python_version, platform, arch]
-    self.python_version = python_version
-    self.python_windows_prefix = python_windows_version_arch_map[arch, python_version]
-    self.manylinux_build = manylinux_build
+    self.labels = ['artifact', 'python', platform, arch, py_version]
+    self.py_version = py_version
 
   def pre_build_jobspecs(self):
     return []
@@ -111,8 +102,8 @@
         environ['SETARCH_CMD'] = 'linux32'
       # Inside the manylinux container, the python installations are located in
       # special places...
-      environ['PYTHON'] = '/opt/python/{}/bin/python'.format(self.manylinux_build)
-      environ['PIP'] = '/opt/python/{}/bin/pip'.format(self.manylinux_build)
+      environ['PYTHON'] = '/opt/python/{}/bin/python'.format(self.py_version)
+      environ['PIP'] = '/opt/python/{}/bin/pip'.format(self.py_version)
       # Platform autodetection for the manylinux1 image breaks so we set the
       # defines ourselves.
       # TODO(atash) get better platform-detection support in core so we don't
@@ -126,14 +117,24 @@
           environ=environ,
           timeout_seconds=60*60)
     elif self.platform == 'windows':
+      if 'Python27' in self.py_version or 'Python34' in self.py_version:
+        environ['EXT_COMPILER'] = 'mingw32'
+      else:
+        environ['EXT_COMPILER'] = 'msvc'
+      # For some reason, the batch script %random% always runs with the same
+      # seed.  We create a random temp-dir here
+      dir = ''.join(random.choice(string.ascii_uppercase) for _ in range(10))
       return create_jobspec(self.name,
                             ['tools\\run_tests\\build_artifact_python.bat',
-                             self.python_windows_prefix,
-                             '32' if self.arch == 'x86' else '64'
+                             self.py_version,
+                             '32' if self.arch == 'x86' else '64',
+                             dir
                             ],
+                            environ=environ,
                             shell=True)
     else:
-      environ['PYTHON'] = 'python{}'.format(self.python_version)
+      environ['PYTHON'] = self.py_version
+      environ['SKIP_PIP_INSTALL'] = 'TRUE'
       return create_jobspec(self.name,
                             ['tools/run_tests/build_artifact_python.sh'],
                             environ=environ)
@@ -330,18 +331,23 @@
            for Cls in (CSharpExtArtifact, NodeExtArtifact, ProtocArtifact)
            for platform in ('linux', 'macos', 'windows')
            for arch in ('x86', 'x64')] +
-          [PythonArtifact('linux', 'x86', '2.7', 'cp27-cp27m'),
-           PythonArtifact('linux', 'x86', '2.7', 'cp27-cp27mu'),
-           PythonArtifact('linux', 'x64', '2.7', 'cp27-cp27m'),
-           PythonArtifact('linux', 'x64', '2.7', 'cp27-cp27mu'),
-           PythonArtifact('macos', 'x64', '2.7'),
-           PythonArtifact('windows', 'x86', '2.7'),
-           PythonArtifact('windows', 'x64', '2.7'),
-           PythonArtifact('linux', 'x86', '3.4', 'cp34-cp34m'),
-           PythonArtifact('linux', 'x64', '3.4', 'cp34-cp34m'),
-           PythonArtifact('macos', 'x64', '3.4'),
-           PythonArtifact('windows', 'x86', '3.4'),
-           PythonArtifact('windows', 'x64', '3.4'),
+          [PythonArtifact('linux', 'x86', 'cp27-cp27m'),
+           PythonArtifact('linux', 'x86', 'cp27-cp27mu'),
+           PythonArtifact('linux', 'x86', 'cp34-cp34m'),
+           PythonArtifact('linux', 'x86', 'cp35-cp35m'),
+           PythonArtifact('linux', 'x64', 'cp27-cp27m'),
+           PythonArtifact('linux', 'x64', 'cp27-cp27mu'),
+           PythonArtifact('linux', 'x64', 'cp34-cp34m'),
+           PythonArtifact('linux', 'x64', 'cp35-cp35m'),
+           PythonArtifact('macos', 'x64', 'python2.7'),
+           PythonArtifact('macos', 'x64', 'python3.4'),
+           PythonArtifact('macos', 'x64', 'python3.5'),
+           PythonArtifact('windows', 'x86', 'Python27_32bits'),
+           PythonArtifact('windows', 'x86', 'Python34_32bits'),
+           PythonArtifact('windows', 'x86', 'Python35_32bits'),
+           PythonArtifact('windows', 'x64', 'Python27'),
+           PythonArtifact('windows', 'x64', 'Python34'),
+           PythonArtifact('windows', 'x64', 'Python35'),
            RubyArtifact('linux', 'x86'),
            RubyArtifact('linux', 'x64'),
            RubyArtifact('macos', 'x64'),
diff --git a/tools/run_tests/build_artifact_php.sh b/tools/run_tests/build_artifact_php.sh
index 50bf0ea..669447f 100755
--- a/tools/run_tests/build_artifact_php.sh
+++ b/tools/run_tests/build_artifact_php.sh
@@ -37,4 +37,4 @@
 
 pear package
 
-cp -r grpc-*.tgz artifacts/grpc-php.tgz
+cp -r grpc-*.tgz artifacts/
diff --git a/tools/run_tests/build_artifact_protoc.bat b/tools/run_tests/build_artifact_protoc.bat
index e1dc032..3246a90 100644
--- a/tools/run_tests/build_artifact_protoc.bat
+++ b/tools/run_tests/build_artifact_protoc.bat
@@ -32,12 +32,8 @@
 setlocal
 cd third_party/protobuf
 
-powershell -Command "Invoke-WebRequest https://googlemock.googlecode.com/files/gmock-1.7.0.zip -OutFile gmock.zip"
-powershell -Command "Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('gmock.zip', '.');"
-rename gmock-1.7.0 gmock
-
 cd cmake
-cmake -G "%generator%" || goto :error
+cmake -G "%generator%" -Dprotobuf_BUILD_TESTS=OFF || goto :error
 endlocal
 
 call vsprojects/build_plugins.bat || goto :error
diff --git a/tools/run_tests/build_artifact_python.bat b/tools/run_tests/build_artifact_python.bat
index 074a3c6..246713a 100644
--- a/tools/run_tests/build_artifact_python.bat
+++ b/tools/run_tests/build_artifact_python.bat
@@ -36,31 +36,43 @@
 
 set GRPC_PYTHON_BUILD_WITH_CYTHON=1
 
+@rem Multiple builds are running simultaneously, so to avoid distutils
+@rem file collisions, we build everything in a tmp directory
+if not exist "artifacts" mkdir "artifacts"
+set ARTIFACT_DIR=%cd%\artifacts
+set BUILD_DIR=C:\Windows\Temp\pygrpc-%3\
+mkdir %BUILD_DIR%
+xcopy /s/e/q %cd%\* %BUILD_DIR%
+pushd %BUILD_DIR%
+
 
 @rem Set up gRPC Python tools
 python tools\distrib\python\make_grpcio_tools.py
 
 @rem Build gRPC Python extensions
-python setup.py build_ext -c mingw32
+python setup.py build_ext -c %EXT_COMPILER% || goto :error
 
 pushd tools\distrib\python\grpcio_tools
-python setup.py build_ext -c mingw32
+python setup.py build_ext -c %EXT_COMPILER% || goto :error
 popd
 
-
 @rem Build gRPC Python distributions
-python setup.py bdist_wheel
+python setup.py bdist_wheel || goto :error
 
 pushd tools\distrib\python\grpcio_tools
-python setup.py bdist_wheel
+python setup.py bdist_wheel || goto :error
 popd
 
 
-mkdir artifacts
-xcopy /Y /I /S dist\* artifacts\ || goto :error
-xcopy /Y /I /S tools\distrib\python\grpcio_tools\dist\* artifacts\ || goto :error
+xcopy /Y /I /S dist\* %ARTIFACT_DIR% || goto :error
+xcopy /Y /I /S tools\distrib\python\grpcio_tools\dist\* %ARTIFACT_DIR% || goto :error
+
+popd
+rmdir /s /q %BUILD_DIR%
 
 goto :EOF
 
 :error
+popd
+rmdir /s /q %BUILD_DIR%
 exit /b 1
diff --git a/tools/run_tests/build_artifact_python.sh b/tools/run_tests/build_artifact_python.sh
index 8f8330e..9fed7c5 100755
--- a/tools/run_tests/build_artifact_python.sh
+++ b/tools/run_tests/build_artifact_python.sh
@@ -38,38 +38,42 @@
 export PIP=${PIP:-pip}
 export AUDITWHEEL=${AUDITWHEEL:-auditwheel}
 
+# Because multiple builds run in parallel, some distutils file
+# operations may collide.  To avoid this, each build is run in
+# a temp directory
+mkdir -p artifacts
+ARTIFACT_DIR="$PWD/artifacts"
+BUILD_DIR=`mktemp -d "${TMPDIR:-/tmp}/pygrpc.XXXXXX"`
+trap "rm -rf $BUILD_DIR" EXIT
+cp -r * "$BUILD_DIR"
+cd "$BUILD_DIR"
 
 # Build the source distribution first because MANIFEST.in cannot override
 # exclusion of built shared objects among package resources (for some
 # inexplicable reason).
-${SETARCH_CMD} ${PYTHON} setup.py  \
-    sdist
+${SETARCH_CMD} ${PYTHON} setup.py sdist
 
 # Wheel has a bug where directories don't get excluded.
 # https://bitbucket.org/pypa/wheel/issues/99/cannot-exclude-directory
-${SETARCH_CMD} ${PYTHON} setup.py  \
-    bdist_wheel
+${SETARCH_CMD} ${PYTHON} setup.py bdist_wheel
 
 # Build gRPC tools package distribution
 ${PYTHON} tools/distrib/python/make_grpcio_tools.py
 
 # Build gRPC tools package source distribution
-${SETARCH_CMD} ${PYTHON} tools/distrib/python/grpcio_tools/setup.py  \
-    sdist
+${SETARCH_CMD} ${PYTHON} tools/distrib/python/grpcio_tools/setup.py sdist
 
 # Build gRPC tools package binary distribution
-CFLAGS="$CFLAGS -fno-wrapv" ${SETARCH_CMD} \
-  ${PYTHON} tools/distrib/python/grpcio_tools/setup.py bdist_wheel
+${SETARCH_CMD} ${PYTHON} tools/distrib/python/grpcio_tools/setup.py bdist_wheel
 
-mkdir -p artifacts
 if [ "$BUILD_MANYLINUX_WHEEL" != "" ]
 then
   for wheel in dist/*.whl; do
-    ${AUDITWHEEL} repair $wheel -w artifacts/
+    ${AUDITWHEEL} repair $wheel -w "$ARTIFACT_DIR"
     rm $wheel
   done
   for wheel in tools/distrib/python/grpcio_tools/dist/*.whl; do
-    ${AUDITWHEEL} repair $wheel -w artifacts/
+    ${AUDITWHEEL} repair $wheel -w "$ARTIFACT_DIR"
     rm $wheel
   done
 fi
@@ -81,14 +85,14 @@
 if [ "$BUILD_HEALTH_CHECKING" != "" ]
 then
   ${PIP} install -rrequirements.txt
-  ${PIP} install grpcio --no-index --find-links "file://${PWD}/artifacts/"
-  ${PIP} install grpcio-tools --no-index --find-links "file://${PWD}/artifacts/"
+  ${PIP} install grpcio --no-index --find-links "file://$ARTIFACT_DIR/"
+  ${PIP} install grpcio-tools --no-index --find-links "file://$ARTIFACT_DIR/"
 
   # Build gRPC health check source distribution
   ${SETARCH_CMD} ${PYTHON} src/python/grpcio_health_checking/setup.py \
       preprocess build_package_protos sdist
-  cp -r src/python/grpcio_health_checking/dist/* artifacts
+  cp -r src/python/grpcio_health_checking/dist/* "$ARTIFACT_DIR"
 fi
 
-cp -r dist/* artifacts
-cp -r tools/distrib/python/grpcio_tools/dist/* artifacts
+cp -r dist/* "$ARTIFACT_DIR"
+cp -r tools/distrib/python/grpcio_tools/dist/* "$ARTIFACT_DIR"
diff --git a/tools/run_tests/build_csharp_coreclr.bat b/tools/run_tests/build_csharp_coreclr.bat
index cead6d0..b6e3ccb 100644
--- a/tools/run_tests/build_csharp_coreclr.bat
+++ b/tools/run_tests/build_csharp_coreclr.bat
@@ -33,7 +33,7 @@
 
 dotnet restore . || goto :error
 
-dotnet build -f netstandard1.5 --configuration %MSBUILD_CONFIG% "**/project.json" || goto :error
+dotnet build --configuration %MSBUILD_CONFIG% "**/project.json" || goto :error
 
 endlocal
 
diff --git a/tools/run_tests/build_csharp_coreclr.sh b/tools/run_tests/build_csharp_coreclr.sh
index 733b1a2..02cf0d3 100755
--- a/tools/run_tests/build_csharp_coreclr.sh
+++ b/tools/run_tests/build_csharp_coreclr.sh
@@ -35,4 +35,4 @@
 # TODO(jtattermusch): introduce caching
 dotnet restore .
 
-dotnet build -f netstandard1.5 --configuration $MSBUILD_CONFIG '**/project.json'
+dotnet build --configuration $MSBUILD_CONFIG '**/project.json'
diff --git a/tools/run_tests/build_package_csharp_coreclr.sh b/tools/run_tests/build_package_csharp_coreclr.sh
deleted file mode 100755
index e1c363d..0000000
--- a/tools/run_tests/build_package_csharp_coreclr.sh
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/bin/bash
-# Copyright 2016, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-set -ex
-
-cd $(dirname $0)/../..
-
-mkdir -p artifacts/
-
-cd src/csharp
-
-# IMPORTANT: NuGet packages generated by dotnet CLI are considered experimental.
-# The official nugets are generated by src/csharp/build_packages.bat
-
-mkdir -p nativelibs/windows_x86 nativelibs/windows_x64 \
-    nativelibs/linux_x86 nativelibs/linux_x64 \
-    nativelibs/macosx_x86 nativelibs/macosx_x64
-
-cp $EXTERNAL_GIT_ROOT/architecture=x86,language=csharp,platform=windows/artifacts/* nativelibs/windows_x86 || true
-cp $EXTERNAL_GIT_ROOT/architecture=x64,language=csharp,platform=windows/artifacts/* nativelibs/windows_x64 || true
-cp $EXTERNAL_GIT_ROOT/architecture=x86,language=csharp,platform=linux/artifacts/* nativelibs/linux_x86 || true
-cp $EXTERNAL_GIT_ROOT/architecture=x64,language=csharp,platform=linux/artifacts/* nativelibs/linux_x64 || true
-cp $EXTERNAL_GIT_ROOT/architecture=x86,language=csharp,platform=macos/artifacts/* nativelibs/macosx_x86 || true
-cp $EXTERNAL_GIT_ROOT/architecture=x64,language=csharp,platform=macos/artifacts/* nativelibs/macosx_x64 || true
-
-dotnet restore .
-
-dotnet pack --configuration Release Grpc.Core/project.json --output ../../artifacts
-dotnet pack --configuration Release Grpc.Auth/project.json --output ../../artifacts
-dotnet pack --configuration Release Grpc.HealthCheck/project.json --output ../../artifacts
-
-tar -czf ../../artifacts/csharp_nugets_experimental.tar.gz ../../artifacts/*.nupkg
diff --git a/tools/run_tests/build_package_node.sh b/tools/run_tests/build_package_node.sh
index ef4a10c..a5636cf 100755
--- a/tools/run_tests/build_package_node.sh
+++ b/tools/run_tests/build_package_node.sh
@@ -49,7 +49,11 @@
 
 mkdir -p bin
 
-cd src/node/tools
+cd $base/src/node/health_check
+npm pack
+cp grpc-health-check-*.tgz $artifacts/
+
+cd $base/src/node/tools
 npm update
 npm pack
 cp grpc-tools-*.tgz $artifacts/
diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh
index c4b5d98..b786c47 100755
--- a/tools/run_tests/build_python.sh
+++ b/tools/run_tests/build_python.sh
@@ -111,6 +111,7 @@
 ROOT=`pwd`
 export CFLAGS="-I$ROOT/include -std=gnu99 -fno-wrapv $CFLAGS"
 export GRPC_PYTHON_BUILD_WITH_CYTHON=1
+export LANG=en_US.UTF-8
 
 # Default python on the host to fall back to when instantiating e.g. the
 # virtualenv.
@@ -155,7 +156,9 @@
   cd $PWD
 }
 
-$VENV_PYTHON -m pip install --upgrade pip setuptools
+$VENV_PYTHON -m pip install --upgrade pip
+# TODO(https://github.com/pypa/setuptools/issues/709) get the latest setuptools
+$VENV_PYTHON -m pip install setuptools==25.1.1
 $VENV_PYTHON -m pip install cython
 pip_install_dir $ROOT
 $VENV_PYTHON $ROOT/tools/distrib/python/make_grpcio_tools.py
diff --git a/tools/run_tests/build_stats_schema.json b/tools/run_tests/build_stats_schema.json
new file mode 100644
index 0000000..021a349
--- /dev/null
+++ b/tools/run_tests/build_stats_schema.json
@@ -0,0 +1,56 @@
+[
+  {
+    "name": "build_number",
+    "type": "INTEGER",
+    "mode": "NULLABLE"
+  },
+  {
+    "name": "timestamp",
+    "type": "TIMESTAMP",
+    "mode": "NULLABLE"
+  },
+  {
+    "name": "matrix",
+    "type": "RECORD",
+    "mode": "REPEATED",
+    "fields": [
+      {
+        "name": "name",
+        "type": "STRING",
+        "mode": "NULLABLE"
+      },
+      {
+        "name": "duration",
+        "type": "FLOAT",
+        "mode": "NULLABLE"
+      },
+      {
+        "name": "pass_count",
+        "type": "INTEGER",
+        "mode": "NULLABLE"
+      },
+      {
+        "name": "failure_count",
+        "type": "INTEGER",
+        "mode": "NULLABLE"
+      },
+      {
+        "name": "error",
+        "type": "RECORD",
+        "mode": "REPEATED",
+        "fields": [
+          {
+            "name": "description",
+            "type": "STRING",
+            "mode": "NULLABLE"
+          },
+          {
+            "name": "count",
+            "type": "INTEGER",
+            "mode": "NULLABLE"
+          }
+        ]
+      }
+    ]
+  }
+]  
diff --git a/tools/run_tests/build_stats_schema_no_matrix.json b/tools/run_tests/build_stats_schema_no_matrix.json
new file mode 100644
index 0000000..42650e3
--- /dev/null
+++ b/tools/run_tests/build_stats_schema_no_matrix.json
@@ -0,0 +1,44 @@
+[
+  {
+    "name": "build_number",
+    "type": "INTEGER",
+    "mode": "NULLABLE"
+  },
+  {
+    "name": "timestamp",
+    "type": "TIMESTAMP",
+    "mode": "NULLABLE"
+  },
+  {
+    "name": "duration",
+    "type": "FLOAT",
+    "mode": "NULLABLE"
+  },
+  {
+    "name": "pass_count",
+    "type": "INTEGER",
+    "mode": "NULLABLE"
+  },
+  {
+    "name": "failure_count",
+    "type": "INTEGER",
+    "mode": "NULLABLE"
+  },
+  {
+    "name": "error",
+    "type": "RECORD",
+    "mode": "REPEATED",
+    "fields": [
+      {
+        "name": "description",
+        "type": "STRING",
+        "mode": "NULLABLE"
+      },
+      {
+        "name": "count",
+        "type": "INTEGER",
+        "mode": "NULLABLE"
+      }
+    ]
+  }
+]  
diff --git a/tools/run_tests/distribtest_targets.py b/tools/run_tests/distribtest_targets.py
index 7930f2a..a16daac 100644
--- a/tools/run_tests/distribtest_targets.py
+++ b/tools/run_tests/distribtest_targets.py
@@ -72,15 +72,22 @@
 class CSharpDistribTest(object):
   """Tests C# NuGet package"""
 
-  def __init__(self, platform, arch, docker_suffix=None):
+  def __init__(self, platform, arch, docker_suffix=None, use_dotnet_cli=False):
     self.name = 'csharp_nuget_%s_%s' % (platform, arch)
     self.platform = platform
     self.arch = arch
     self.docker_suffix = docker_suffix
     self.labels = ['distribtest', 'csharp', platform, arch]
+    self.script_suffix = ''
     if docker_suffix:
       self.name += '_%s' % docker_suffix
       self.labels.append(docker_suffix)
+    if use_dotnet_cli:
+      self.name += '_dotnetcli'
+      self.script_suffix = '_dotnetcli'
+      self.labels.append('dotnetcli')
+    else:
+      self.labels.append('olddotnet')
 
   def pre_build_jobspecs(self):
     return []
@@ -91,10 +98,10 @@
           'tools/dockerfile/distribtest/csharp_%s_%s' % (
               self.docker_suffix,
               self.arch),
-          'test/distrib/csharp/run_distrib_test.sh')
+          'test/distrib/csharp/run_distrib_test%s.sh' % self.script_suffix)
     elif self.platform == 'macos':
       return create_jobspec(self.name,
-          ['test/distrib/csharp/run_distrib_test.sh'],
+          ['test/distrib/csharp/run_distrib_test%s.sh' % self.script_suffix],
           environ={'EXTERNAL_GIT_ROOT': '../../..'})
     elif self.platform == 'windows':
       if self.arch == 'x64':
@@ -103,7 +110,7 @@
       else:
         environ={'DISTRIBTEST_OUTPATH': 'DistribTest\\bin\\\Debug'}
       return create_jobspec(self.name,
-          ['test\\distrib\\csharp\\run_distrib_test.bat'],
+          ['test\\distrib\\csharp\\run_distrib_test%s.bat' % self.script_suffix],
           environ=environ)
     else:
       raise Exception("Not supported yet.")
@@ -276,6 +283,7 @@
           CSharpDistribTest('linux', 'x64', 'ubuntu1504'),
           CSharpDistribTest('linux', 'x64', 'ubuntu1510'),
           CSharpDistribTest('linux', 'x64', 'ubuntu1604'),
+          CSharpDistribTest('linux', 'x64', 'ubuntu1404', use_dotnet_cli=True),
           CSharpDistribTest('macos', 'x86'),
           CSharpDistribTest('windows', 'x86'),
           CSharpDistribTest('windows', 'x64'),
diff --git a/tools/run_tests/dockerize/build_docker_and_run_tests.sh b/tools/run_tests/dockerize/build_docker_and_run_tests.sh
index c2ea6f2..b4b172d 100755
--- a/tools/run_tests/dockerize/build_docker_and_run_tests.sh
+++ b/tools/run_tests/dockerize/build_docker_and_run_tests.sh
@@ -44,9 +44,6 @@
 # its cache location now that --download-cache is deprecated).
 mkdir -p /tmp/xdg-cache-home
 
-# Create a local branch so the child Docker script won't complain
-git branch -f jenkins-docker
-
 # Inputs
 # DOCKERFILE_DIR - Directory in which Dockerfile file is located.
 # DOCKER_RUN_SCRIPT - Script to run under docker (relative to grpc repo root)
@@ -86,9 +83,12 @@
   $DOCKER_IMAGE_NAME \
   bash -l "/var/local/jenkins/grpc/$DOCKER_RUN_SCRIPT" || DOCKER_FAILED="true"
 
-docker cp "$CONTAINER_NAME:/var/local/git/grpc/reports.zip" $git_root || true
-unzip -o $git_root/reports.zip -d $git_root || true
-rm -f reports.zip
+# use unique name for reports.zip to prevent clash between concurrent
+# run_tests.py runs 
+TEMP_REPORTS_ZIP=`mktemp`
+docker cp "$CONTAINER_NAME:/var/local/git/grpc/reports.zip" ${TEMP_REPORTS_ZIP} || true
+unzip -o ${TEMP_REPORTS_ZIP} -d $git_root || true
+rm -f ${TEMP_REPORTS_ZIP}
 
 # remove the container, possibly killing it first
 docker rm -f $CONTAINER_NAME || true
diff --git a/tools/run_tests/dockerize/docker_run_tests.sh b/tools/run_tests/dockerize/docker_run_tests.sh
index 8c6143d..ef02d26 100755
--- a/tools/run_tests/dockerize/docker_run_tests.sh
+++ b/tools/run_tests/dockerize/docker_run_tests.sh
@@ -63,6 +63,7 @@
 cd ..
 
 zip -r reports.zip reports
-find . -name report.xml | xargs zip reports.zip
+find . -name report.xml | xargs -r zip reports.zip
+find . -name 'report_*.xml' | xargs -r zip reports.zip
 
 exit $exit_code
diff --git a/tools/run_tests/package_targets.py b/tools/run_tests/package_targets.py
index ce3f08d..abbb5fa 100644
--- a/tools/run_tests/package_targets.py
+++ b/tools/run_tests/package_targets.py
@@ -71,24 +71,31 @@
 class CSharpPackage:
   """Builds C# nuget packages."""
 
-  def __init__(self, use_coreclr=False):
-    self.use_coreclr = use_coreclr
-    self.name = 'csharp_package_coreclr' if use_coreclr else 'csharp_package'
+  def __init__(self, use_dotnet_cli=False):
+    self.use_dotnet_cli = use_dotnet_cli
+    self.name = 'csharp_package_dotnetcli' if use_dotnet_cli else 'csharp_package'
     self.labels = ['package', 'csharp']
-    if use_coreclr:
+    if use_dotnet_cli:
       self.labels += ['linux']
     else:
       self.labels += ['windows']
 
   def pre_build_jobspecs(self):
-    return []
+    if 'windows' in self.labels:
+      return [create_jobspec('prebuild_%s' % self.name,
+                             ['tools\\run_tests\\pre_build_csharp.bat'],
+                             shell=True,
+                             flake_retries=5,
+                             timeout_retries=2)]
+    else:
+      return []
 
   def build_jobspec(self):
-    if self.use_coreclr:
+    if self.use_dotnet_cli:
       return create_docker_jobspec(
           self.name,
           'tools/dockerfile/test/csharp_coreclr_x64',
-          'tools/run_tests/build_package_csharp_coreclr.sh')
+          'src/csharp/build_packages_dotnetcli.sh')
     else:
       return create_jobspec(self.name,
                             ['build_packages.bat'],
@@ -170,7 +177,7 @@
 def targets():
   """Gets list of supported targets"""
   return [CSharpPackage(),
-          CSharpPackage(use_coreclr=True),
+          CSharpPackage(use_dotnet_cli=True),
           NodePackage(),
           RubyPackage(),
           PythonPackage(),
diff --git a/tools/run_tests/performance/scenario_config.py b/tools/run_tests/performance/scenario_config.py
index 4dfd01f..725ca29 100644
--- a/tools/run_tests/performance/scenario_config.py
+++ b/tools/run_tests/performance/scenario_config.py
@@ -29,12 +29,16 @@
 
 # performance scenario configuration for various languages
 
+import math
+
 WARMUP_SECONDS=5
 JAVA_WARMUP_SECONDS=15  # Java needs more warmup time for JIT to kick in.
 BENCHMARK_SECONDS=30
 
 SMOKETEST='smoketest'
 SCALABLE='scalable'
+SWEEP='sweep'
+DEFAULT_CATEGORIES=[SCALABLE, SMOKETEST]
 
 SECURE_SECARGS = {'use_test_ca': True,
                   'server_host_override': 'foo.test.google.fr'}
@@ -63,17 +67,17 @@
   }
 }
 
-# deep is the number of RPCs outstanding on a channel in non-ping-pong tests
-# (the value used is 1 otherwise)
-DEEP=100
+# target number of RPCs outstanding on across all client channels in
+# non-ping-pong tests (since we can only specify per-channel numbers, the
+# actual target will be slightly higher)
+OUTSTANDING_REQUESTS={
+    'async': 6400,
+    'sync': 1000
+}
 
 # wide is the number of client channels in multi-channel tests (1 otherwise)
 WIDE=64
 
-# For most synchronous clients, DEEP*WIDE threads will be created.
-SYNC_DEEP=10
-SYNC_WIDE=8
-
 
 def _get_secargs(is_secure):
   if is_secure:
@@ -91,6 +95,13 @@
   return scenario
 
 
+def geometric_progression(start, stop, step):
+  n = start
+  while n < stop:
+    yield int(round(n))
+    n *= step
+
+
 def _ping_pong_scenario(name, rpc_type,
                         client_type, server_type,
                         secure=True,
@@ -101,7 +112,9 @@
                         server_core_limit=0,
                         async_server_threads=0,
                         warmup_seconds=WARMUP_SECONDS,
-                        categories=[]):
+                        categories=DEFAULT_CATEGORIES,
+                        channels=None,
+                        outstanding=None):
   """Creates a basic ping pong scenario."""
   scenario = {
     'name': name,
@@ -138,14 +151,9 @@
     scenario['client_config']['payload_config'] = EMPTY_PROTO_PAYLOAD
 
   if unconstrained_client:
-    if unconstrained_client == 'async':
-      deep = DEEP
-      wide = WIDE
-    elif unconstrained_client == 'sync':
-      deep = SYNC_DEEP
-      wide = SYNC_WIDE
-    else:
-      raise Exception('Illegal value of unconstrained_client option.')
+    outstanding_calls = outstanding if outstanding is not None else OUTSTANDING_REQUESTS[unconstrained_client]
+    wide = channels if channels is not None else WIDE
+    deep = int(math.ceil(1.0 * outstanding_calls / wide))
 
     scenario['num_clients'] = 0  # use as many client as available.
     scenario['client_config']['outstanding_rpcs_per_channel'] = deep
@@ -182,62 +190,66 @@
     # TODO(ctiller): add 70% load latency test
     for secure in [True, False]:
       secstr = 'secure' if secure else 'insecure'
-      smoketest_categories = [SMOKETEST] if secure else []
+      smoketest_categories = ([SMOKETEST] if secure else []) + [SCALABLE]
 
       yield _ping_pong_scenario(
-          'cpp_generic_async_streaming_ping_pong_%s' % secstr, rpc_type='STREAMING',
-          client_type='ASYNC_CLIENT', server_type='ASYNC_GENERIC_SERVER',
+          'cpp_generic_async_streaming_ping_pong_%s' % secstr,
+          rpc_type='STREAMING',
+          client_type='ASYNC_CLIENT',
+          server_type='ASYNC_GENERIC_SERVER',
           use_generic_payload=True, server_core_limit=1, async_server_threads=1,
           secure=secure,
           categories=smoketest_categories)
 
       yield _ping_pong_scenario(
-          'cpp_protobuf_async_streaming_ping_pong_%s' % secstr, rpc_type='STREAMING',
-          client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
-          server_core_limit=1, async_server_threads=1,
-          secure=secure)
-
-      yield _ping_pong_scenario(
-          'cpp_protobuf_async_unary_ping_pong_%s' % secstr, rpc_type='UNARY',
-          client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
-          server_core_limit=1, async_server_threads=1,
-          secure=secure,
-          categories=smoketest_categories)
-
-      yield _ping_pong_scenario(
-          'cpp_protobuf_sync_unary_ping_pong_%s' % secstr, rpc_type='UNARY',
-          client_type='SYNC_CLIENT', server_type='SYNC_SERVER',
-          server_core_limit=1, async_server_threads=1,
-          secure=secure)
-
-      yield _ping_pong_scenario(
-          'cpp_protobuf_async_unary_qps_unconstrained_%s' % secstr, rpc_type='UNARY',
-          client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
-          unconstrained_client='async',
-          secure=secure,
-          categories=smoketest_categories+[SCALABLE])
-
-      yield _ping_pong_scenario(
-          'cpp_protobuf_async_streaming_qps_unconstrained_%s' % secstr, rpc_type='STREAMING',
-          client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
-          unconstrained_client='async',
-          secure=secure,
-          categories=[SCALABLE])
-
-      yield _ping_pong_scenario(
-          'cpp_generic_async_streaming_qps_unconstrained_%s' % secstr, rpc_type='STREAMING',
-          client_type='ASYNC_CLIENT', server_type='ASYNC_GENERIC_SERVER',
+          'cpp_generic_async_streaming_qps_unconstrained_%s' % secstr,
+          rpc_type='STREAMING',
+          client_type='ASYNC_CLIENT',
+          server_type='ASYNC_GENERIC_SERVER',
           unconstrained_client='async', use_generic_payload=True,
           secure=secure,
           categories=smoketest_categories+[SCALABLE])
 
       yield _ping_pong_scenario(
-          'cpp_generic_async_streaming_qps_one_server_core_%s' % secstr, rpc_type='STREAMING',
-          client_type='ASYNC_CLIENT', server_type='ASYNC_GENERIC_SERVER',
+          'cpp_generic_async_streaming_qps_one_server_core_%s' % secstr,
+          rpc_type='STREAMING',
+          client_type='ASYNC_CLIENT',
+          server_type='ASYNC_GENERIC_SERVER',
           unconstrained_client='async', use_generic_payload=True,
           server_core_limit=1, async_server_threads=1,
           secure=secure)
 
+      for rpc_type in ['unary', 'streaming']:
+        for synchronicity in ['sync', 'async']:
+          yield _ping_pong_scenario(
+              'cpp_protobuf_%s_%s_ping_pong_%s' % (synchronicity, rpc_type, secstr),
+              rpc_type=rpc_type.upper(),
+              client_type='%s_CLIENT' % synchronicity.upper(),
+              server_type='%s_SERVER' % synchronicity.upper(),
+              server_core_limit=1, async_server_threads=1,
+              secure=secure)
+
+          yield _ping_pong_scenario(
+              'cpp_protobuf_%s_%s_qps_unconstrained_%s' % (synchronicity, rpc_type, secstr),
+              rpc_type=rpc_type.upper(),
+              client_type='%s_CLIENT' % synchronicity.upper(),
+              server_type='%s_SERVER' % synchronicity.upper(),
+              unconstrained_client=synchronicity,
+              secure=secure,
+              categories=smoketest_categories+[SCALABLE])
+
+          for channels in geometric_progression(1, 20000, math.sqrt(10)):
+            for outstanding in geometric_progression(1, 200000, math.sqrt(10)):
+                if synchronicity == 'sync' and outstanding > 1200: continue
+                if outstanding < channels: continue
+                yield _ping_pong_scenario(
+                    'cpp_protobuf_%s_%s_qps_unconstrained_%s_%d_channels_%d_outstanding' % (synchronicity, rpc_type, secstr, channels, outstanding),
+                    rpc_type=rpc_type.upper(),
+                    client_type='%s_CLIENT' % synchronicity.upper(),
+                    server_type='%s_SERVER' % synchronicity.upper(),
+                    unconstrained_client=synchronicity, secure=secure,
+                    categories=[SWEEP], channels=channels, outstanding=outstanding)
+
   def __str__(self):
     return 'c++'
 
@@ -258,7 +270,7 @@
         'csharp_generic_async_streaming_ping_pong', rpc_type='STREAMING',
         client_type='ASYNC_CLIENT', server_type='ASYNC_GENERIC_SERVER',
         use_generic_payload=True,
-        categories=[SMOKETEST])
+        categories=[SMOKETEST, SCALABLE])
 
     yield _ping_pong_scenario(
         'csharp_protobuf_async_streaming_ping_pong', rpc_type='STREAMING',
@@ -267,7 +279,7 @@
     yield _ping_pong_scenario(
         'csharp_protobuf_async_unary_ping_pong', rpc_type='UNARY',
         client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
-        categories=[SMOKETEST])
+        categories=[SMOKETEST, SCALABLE])
 
     yield _ping_pong_scenario(
         'csharp_protobuf_sync_to_async_unary_ping_pong', rpc_type='UNARY',
@@ -289,7 +301,7 @@
         'csharp_to_cpp_protobuf_sync_unary_ping_pong', rpc_type='UNARY',
         client_type='SYNC_CLIENT', server_type='SYNC_SERVER',
         server_language='c++', server_core_limit=1, async_server_threads=1,
-        categories=[SMOKETEST])
+        categories=[SMOKETEST, SCALABLE])
 
     yield _ping_pong_scenario(
         'csharp_to_cpp_protobuf_async_streaming_ping_pong', rpc_type='STREAMING',
@@ -346,13 +358,13 @@
     yield _ping_pong_scenario(
         'node_protobuf_unary_ping_pong', rpc_type='UNARY',
         client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
-        categories=[SMOKETEST])
+        categories=[SCALABLE, SMOKETEST])
 
     yield _ping_pong_scenario(
         'node_protobuf_async_unary_qps_unconstrained', rpc_type='UNARY',
         client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER',
         unconstrained_client='async',
-        categories=[SMOKETEST])
+        categories=[SCALABLE, SMOKETEST])
 
     # TODO(jtattermusch): make this scenario work
     #yield _ping_pong_scenario(
@@ -391,7 +403,7 @@
         'python_generic_sync_streaming_ping_pong', rpc_type='STREAMING',
         client_type='SYNC_CLIENT', server_type='ASYNC_GENERIC_SERVER',
         use_generic_payload=True,
-        categories=[SMOKETEST])
+        categories=[SMOKETEST, SCALABLE])
 
     yield _ping_pong_scenario(
         'python_protobuf_sync_streaming_ping_pong', rpc_type='STREAMING',
@@ -404,7 +416,7 @@
     yield _ping_pong_scenario(
         'python_protobuf_sync_unary_ping_pong', rpc_type='UNARY',
         client_type='SYNC_CLIENT', server_type='ASYNC_SERVER',
-        categories=[SMOKETEST])
+        categories=[SMOKETEST, SCALABLE])
 
     yield _ping_pong_scenario(
         'python_protobuf_sync_unary_qps_unconstrained', rpc_type='UNARY',
@@ -420,7 +432,7 @@
         'python_to_cpp_protobuf_sync_unary_ping_pong', rpc_type='UNARY',
         client_type='SYNC_CLIENT', server_type='ASYNC_SERVER',
         server_language='c++', server_core_limit=1, async_server_threads=1,
-        categories=[SMOKETEST])
+        categories=[SMOKETEST, SCALABLE])
 
     yield _ping_pong_scenario(
         'python_to_cpp_protobuf_sync_streaming_ping_pong', rpc_type='STREAMING',
@@ -446,12 +458,12 @@
     yield _ping_pong_scenario(
         'ruby_protobuf_sync_streaming_ping_pong', rpc_type='STREAMING',
         client_type='SYNC_CLIENT', server_type='SYNC_SERVER',
-        categories=[SMOKETEST])
+        categories=[SMOKETEST, SCALABLE])
 
     yield _ping_pong_scenario(
         'ruby_protobuf_unary_ping_pong', rpc_type='UNARY',
         client_type='SYNC_CLIENT', server_type='SYNC_SERVER',
-        categories=[SMOKETEST])
+        categories=[SMOKETEST, SCALABLE])
 
     yield _ping_pong_scenario(
         'ruby_protobuf_sync_unary_qps_unconstrained', rpc_type='UNARY',
@@ -492,7 +504,7 @@
   def scenarios(self):
     for secure in [True, False]:
       secstr = 'secure' if secure else 'insecure'
-      smoketest_categories = [SMOKETEST] if secure else []
+      smoketest_categories = ([SMOKETEST] if secure else []) + [SCALABLE]
 
       yield _ping_pong_scenario(
           'java_generic_async_streaming_ping_pong_%s' % secstr, rpc_type='STREAMING',
@@ -548,7 +560,7 @@
           async_server_threads=1,
           secure=secure, warmup_seconds=JAVA_WARMUP_SECONDS)
 
-      # TODO(jtattermusch): add scenarios java vs C++ 
+      # TODO(jtattermusch): add scenarios java vs C++
 
   def __str__(self):
     return 'java'
@@ -569,10 +581,10 @@
   def scenarios(self):
     for secure in [True, False]:
       secstr = 'secure' if secure else 'insecure'
-      smoketest_categories = [SMOKETEST] if secure else []
+      smoketest_categories = ([SMOKETEST] if secure else []) + [SCALABLE]
 
       # ASYNC_GENERIC_SERVER for Go actually uses a sync streaming server,
-      # but that's mostly because of lack of better name of the enum value. 
+      # but that's mostly because of lack of better name of the enum value.
       yield _ping_pong_scenario(
           'go_generic_sync_streaming_ping_pong_%s' % secstr, rpc_type='STREAMING',
           client_type='SYNC_CLIENT', server_type='ASYNC_GENERIC_SERVER',
@@ -619,7 +631,7 @@
           secure=secure,
           categories=[SCALABLE])
 
-      # TODO(jtattermusch): add scenarios go vs C++ 
+      # TODO(jtattermusch): add scenarios go vs C++
 
   def __str__(self):
     return 'go'
diff --git a/tools/run_tests/pre_build_csharp.bat b/tools/run_tests/pre_build_csharp.bat
index 580d563..f15979a 100644
--- a/tools/run_tests/pre_build_csharp.bat
+++ b/tools/run_tests/pre_build_csharp.bat
@@ -38,9 +38,10 @@
 set NUGET=C:\nuget\nuget.exe
 
 if exist %NUGET% (
+  @rem TODO(jtattermusch): Get rid of this hack. See #8034
   @rem Restore Grpc packages by packages since Nuget client 3.4.4 doesnt support restore
   @rem by solution
-  @rem Moving into each directory to let the restores work with both nuget 3.4 and 2.8
+  @rem Moving into each directory to let the restores work based on per-project packages.config files
   %NUGET% restore vsprojects/grpc_csharp_ext.sln || goto :error
 
   cd src/csharp
diff --git a/tools/run_tests/pre_build_csharp.sh b/tools/run_tests/pre_build_csharp.sh
index 0fd3b92..ee678dd 100755
--- a/tools/run_tests/pre_build_csharp.sh
+++ b/tools/run_tests/pre_build_csharp.sh
@@ -37,9 +37,10 @@
 
 if [ -x "$(command -v nuget)" ]
 then
+  # TODO(jtattermusch): Get rid of this hack. See #8034
   # Restoring Nuget packages by packages rather than by solution because of
   # inability to restore by solution with Nuget client 3.4.4
-  # Moving into each directory to let the restores work with nuget 3.4 and 2.8
+  # Moving into each directory to let the restores work based on per-project packages.config files
   cd Grpc.Auth
   nuget restore -PackagesDirectory ../packages
   cd ..
diff --git a/tools/run_tests/run_build_statistics.py b/tools/run_tests/run_build_statistics.py
new file mode 100755
index 0000000..32f7bc0
--- /dev/null
+++ b/tools/run_tests/run_build_statistics.py
@@ -0,0 +1,223 @@
+#!/usr/bin/env python2.7
+# Copyright 2016, 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.
+
+"""Tool to get build statistics from Jenkins and upload to BigQuery."""
+
+import argparse
+import jenkinsapi
+from jenkinsapi.custom_exceptions import JenkinsAPIException
+from jenkinsapi.jenkins import Jenkins
+import json
+import os
+import re
+import sys
+import urllib
+
+
+gcp_utils_dir = os.path.abspath(os.path.join(
+    os.path.dirname(__file__), '../gcp/utils'))
+sys.path.append(gcp_utils_dir)
+import big_query_utils
+
+
+_HAS_MATRIX=True
+_PROJECT_ID = 'grpc-testing'
+_HAS_MATRIX = True
+_BUILDS = {'gRPC_master': _HAS_MATRIX, 
+           'gRPC_interop_master': not _HAS_MATRIX, 
+           'gRPC_pull_requests': _HAS_MATRIX, 
+           'gRPC_interop_pull_requests': not _HAS_MATRIX,
+}
+_URL_BASE = 'https://grpc-testing.appspot.com/job'
+
+# This is a dynamic list where known and active issues should be added. 
+# Fixed ones should be removed.
+# Also try not to add multiple messages from the same failure.
+_KNOWN_ERRORS = [
+    'Failed to build workspace Tests with scheme AllTests',
+    'Build timed out',
+    'TIMEOUT: tools/run_tests/pre_build_node.sh',
+    'TIMEOUT: tools/run_tests/pre_build_ruby.sh',
+    'FATAL: Unable to produce a script file',
+    'FAILED: build_docker_c\+\+',
+    'cannot find package \"cloud.google.com/go/compute/metadata\"',
+    'LLVM ERROR: IO failure on output stream.',
+    'MSBUILD : error MSB1009: Project file does not exist.',
+    'fatal: git fetch_pack: expected ACK/NAK',
+    'Failed to fetch from http://github.com/grpc/grpc.git',
+    ('hudson.remoting.RemotingSystemException: java.io.IOException: '
+     'Backing channel is disconnected.'),
+    'hudson.remoting.ChannelClosedException',
+    'Could not initialize class hudson.Util',
+    'Too many open files in system',
+    'FAILED: bins/tsan/qps_openloop_test GRPC_POLL_STRATEGY=epoll',
+    'FAILED: bins/tsan/qps_openloop_test GRPC_POLL_STRATEGY=legacy',
+    'FAILED: bins/tsan/qps_openloop_test GRPC_POLL_STRATEGY=poll',
+    ('tests.bins/asan/h2_proxy_test streaming_error_response '
+     'GRPC_POLL_STRATEGY=legacy'),
+]
+_UNKNOWN_ERROR = 'Unknown error'
+_DATASET_ID = 'build_statistics'
+
+
+def _scrape_for_known_errors(html):
+  error_list = []
+  known_error_count = 0
+  for known_error in _KNOWN_ERRORS:
+    errors = re.findall(known_error, html)
+    this_error_count = len(errors)
+    if this_error_count > 0: 
+      known_error_count += this_error_count
+      error_list.append({'description': known_error,
+                         'count': this_error_count})
+      print('====> %d failures due to %s' % (this_error_count, known_error))
+  return error_list, known_error_count
+
+
+def _get_last_processed_buildnumber(build_name):
+  query = 'SELECT max(build_number) FROM [%s:%s.%s];' % (
+      _PROJECT_ID, _DATASET_ID, build_name)
+  query_job = big_query_utils.sync_query_job(bq, _PROJECT_ID, query)
+  page = bq.jobs().getQueryResults(
+      pageToken=None,
+      **query_job['jobReference']).execute(num_retries=3)
+  if page['rows'][0]['f'][0]['v']:
+    return int(page['rows'][0]['f'][0]['v'])
+  return 0
+
+
+def _process_matrix(build, url_base):
+  matrix_list = []
+  for matrix in build.get_matrix_runs():
+    matrix_str = re.match('.*\\xc2\\xbb ((?:[^,]+,?)+) #.*', 
+                          matrix.name).groups()[0]
+    matrix_tuple = matrix_str.split(',')
+    json_url = '%s/config=%s,language=%s,platform=%s/testReport/api/json' % (
+        url_base, matrix_tuple[0], matrix_tuple[1], matrix_tuple[2])
+    console_url = '%s/config=%s,language=%s,platform=%s/consoleFull' % (
+        url_base, matrix_tuple[0], matrix_tuple[1], matrix_tuple[2])
+    matrix_dict = {'name': matrix_str,
+                   'duration': matrix.get_duration().total_seconds()}
+    matrix_dict.update(_process_build(json_url, console_url))
+    matrix_list.append(matrix_dict)
+
+  return matrix_list 
+
+
+def _process_build(json_url, console_url):
+  build_result = {}
+  error_list = []
+  try:
+    html = urllib.urlopen(json_url).read()
+    test_result = json.loads(html)
+    print('====> Parsing result from %s' % json_url)
+    failure_count = test_result['failCount']
+    build_result['pass_count'] = test_result['passCount']
+    build_result['failure_count'] = failure_count
+    if failure_count > 0:
+      error_list, known_error_count = _scrape_for_known_errors(html)
+      unknown_error_count = failure_count - known_error_count
+      # This can happen if the same error occurs multiple times in one test.
+      if failure_count < known_error_count:
+        print('====> Some errors are duplicates.')
+        unknown_error_count = 0
+      error_list.append({'description': _UNKNOWN_ERROR, 
+                         'count': unknown_error_count})
+  except Exception as e:
+    print('====> Got exception for %s: %s.' % (json_url, str(e)))   
+    print('====> Parsing errors from %s.' % console_url)
+    html = urllib.urlopen(console_url).read()
+    build_result['pass_count'] = 0  
+    build_result['failure_count'] = 1
+    error_list, _ = _scrape_for_known_errors(html)
+    if error_list:
+      error_list.append({'description': _UNKNOWN_ERROR, 'count': 0})
+    else:
+      error_list.append({'description': _UNKNOWN_ERROR, 'count': 1})
+ 
+  if error_list:
+    build_result['error'] = error_list
+
+  return build_result 
+
+
+# parse command line
+argp = argparse.ArgumentParser(description='Get build statistics.')
+argp.add_argument('-u', '--username', default='jenkins')
+argp.add_argument('-b', '--builds', 
+                  choices=['all'] + sorted(_BUILDS.keys()),
+                  nargs='+',
+                  default=['all'])
+args = argp.parse_args()
+
+J = Jenkins('https://grpc-testing.appspot.com', args.username, 'apiToken')
+bq = big_query_utils.create_big_query()
+
+for build_name in _BUILDS.keys() if 'all' in args.builds else args.builds:
+  print('====> Build: %s' % build_name)
+  # Since get_last_completed_build() always fails due to malformatted string
+  # error, we use get_build_metadata() instead.
+  job = None
+  try:
+    job = J[build_name]
+  except Exception as e:
+    print('====> Failed to get build %s: %s.' % (build_name, str(e)))
+    continue
+  last_processed_build_number = _get_last_processed_buildnumber(build_name)
+  last_complete_build_number = job.get_last_completed_buildnumber()
+  # To avoid processing all builds for a project never looked at. In this case,
+  # only examine 10 latest builds.
+  starting_build_number = max(last_processed_build_number+1, 
+                              last_complete_build_number-9)
+  for build_number in xrange(starting_build_number, 
+                             last_complete_build_number+1):
+    print('====> Processing %s build %d.' % (build_name, build_number))
+    build = None
+    try:
+      build = job.get_build_metadata(build_number)
+    except KeyError:
+      print('====> Build %s is missing. Skip.' % build_number)
+      continue
+    build_result = {'build_number': build_number, 
+                    'timestamp': str(build.get_timestamp())}
+    url_base = json_url = '%s/%s/%d' % (_URL_BASE, build_name, build_number)
+    if _BUILDS[build_name]:  # The build has matrix, such as gRPC_master.
+      build_result['matrix'] = _process_matrix(build, url_base)
+    else:
+      json_url = '%s/testReport/api/json' % url_base
+      console_url = '%s/consoleFull' % url_base
+      build_result['duration'] = build.get_duration().total_seconds()
+      build_result.update(_process_build(json_url, console_url))
+    rows = [big_query_utils.make_row(build_number, build_result)]
+    if not big_query_utils.insert_rows(bq, _PROJECT_ID, _DATASET_ID, build_name, 
+                                       rows):
+      print '====> Error uploading result to bigquery.'
+      sys.exit(1)
+
diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py
index 78096b2..389d070 100755
--- a/tools/run_tests/run_interop_tests.py
+++ b/tools/run_tests/run_interop_tests.py
@@ -127,6 +127,35 @@
     return 'csharp'
 
 
+class CSharpCoreCLRLanguage:
+
+  def __init__(self):
+    self.client_cwd = 'src/csharp/Grpc.IntegrationTesting.Client/bin/Debug/netcoreapp1.0'
+    self.server_cwd = 'src/csharp/Grpc.IntegrationTesting.Server/bin/Debug/netcoreapp1.0'
+    self.safename = str(self)
+
+  def client_cmd(self, args):
+    return ['dotnet', 'exec', 'Grpc.IntegrationTesting.Client.dll'] + args
+
+  def cloud_to_prod_env(self):
+    return {}
+
+  def server_cmd(self, args):
+    return ['dotnet', 'exec', 'Grpc.IntegrationTesting.Server.dll', '--use_tls=true'] + args
+
+  def global_env(self):
+    return {}
+
+  def unimplemented_test_cases(self):
+    return _SKIP_SERVER_COMPRESSION
+
+  def unimplemented_test_cases_server(self):
+    return _SKIP_COMPRESSION
+
+  def __str__(self):
+    return 'csharpcoreclr'
+
+
 class JavaLanguage:
 
   def __init__(self):
@@ -367,6 +396,7 @@
 _LANGUAGES = {
     'c++' : CXXLanguage(),
     'csharp' : CSharpLanguage(),
+    'csharpcoreclr' : CSharpCoreCLRLanguage(),
     'go' : GoLanguage(),
     'java' : JavaLanguage(),
     'node' : NodeLanguage(),
@@ -377,7 +407,7 @@
 }
 
 # languages supported as cloud_to_cloud servers
-_SERVERS = ['c++', 'node', 'csharp', 'java', 'go', 'ruby', 'python']
+_SERVERS = ['c++', 'node', 'csharp', 'csharpcoreclr', 'java', 'go', 'ruby', 'python']
 
 _TEST_CASES = ['large_unary', 'empty_unary', 'ping_pong',
                'empty_stream', 'client_streaming', 'server_streaming',
@@ -435,7 +465,7 @@
   default_account_arg = '--default_service_account=830293263384-compute@developer.gserviceaccount.com'
 
   if test_case in ['jwt_token_creds', 'per_rpc_creds', 'oauth2_auth_token']:
-    if language in ['csharp', 'node', 'php', 'php7', 'python', 'ruby']:
+    if language in ['csharp', 'csharpcoreclr', 'node', 'php', 'php7', 'python', 'ruby']:
       env['GOOGLE_APPLICATION_CREDENTIALS'] = key_filepath
     else:
       cmdargs += [key_file_arg]
@@ -623,15 +653,15 @@
 # TODO(adelez): implement logic for errors_allowed where if the indicated tests
 # fail, they don't impact the overall test result.
 prod_servers = {
-    'default': ('grpc-test.sandbox.googleapis.com',
+    'default': ('216.239.32.254',
                 'grpc-test.sandbox.googleapis.com', False),
-    'gateway_v2': ('grpc-test2.sandbox.googleapis.com',
+    'gateway_v2': ('216.239.32.254',
                    'grpc-test2.sandbox.googleapis.com', True),
     'cloud_gateway': ('216.239.32.255', 'grpc-test.sandbox.googleapis.com',
                       False),
     'cloud_gateway_v2': ('216.239.32.255', 'grpc-test2.sandbox.googleapis.com',
                          True),
-    'gateway_v4': ('grpc-test4.sandbox.googleapis.com',
+    'gateway_v4': ('216.239.32.254',
                    'grpc-test4.sandbox.googleapis.com', True),
     'cloud_gateway_v4': ('216.239.32.255', 'grpc-test4.sandbox.googleapis.com',
                          True),
@@ -662,7 +692,7 @@
                         'cloud_to_prod_auth tests against.'))
 argp.add_argument('-s', '--server',
                   choices=['all'] + sorted(_SERVERS),
-                  action='append',
+                  nargs='+',
                   help='Run cloud_to_cloud servers in a separate docker ' +
                        'image. Servers can only be started automatically if ' +
                        '--use_docker option is enabled.',
diff --git a/tools/run_tests/run_performance_tests.py b/tools/run_tests/run_performance_tests.py
index 5fdf7a4..9214911 100755
--- a/tools/run_tests/run_performance_tests.py
+++ b/tools/run_tests/run_performance_tests.py
@@ -33,11 +33,13 @@
 from __future__ import print_function
 
 import argparse
+import collections
 import itertools
 import jobset
 import json
 import multiprocessing
 import os
+import performance.scenario_config as scenario_config
 import pipes
 import re
 import subprocess
@@ -46,7 +48,6 @@
 import time
 import traceback
 import uuid
-import performance.scenario_config as scenario_config
 
 
 _ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
@@ -63,14 +64,19 @@
     self._spec = spec
     self.language = language
     self.host_and_port = host_and_port
-    self._job = jobset.Job(spec, newline_on_success=True, travis=True, add_env={})
+    self._job = None
+
+  def start(self):
+    self._job = jobset.Job(self._spec, newline_on_success=True, travis=True, add_env={})
 
   def is_running(self):
     """Polls a job and returns True if given job is still running."""
-    return self._job.state() == jobset._RUNNING
+    return self._job and self._job.state() == jobset._RUNNING
 
   def kill(self):
-    return self._job.kill()
+    if self._job:
+      self._job.kill()
+      self._job = None
 
 
 def create_qpsworker_job(language, shortname=None,
@@ -119,7 +125,7 @@
 def create_quit_jobspec(workers, remote_host=None):
   """Runs quit using QPS driver."""
   # setting QPS_WORKERS env variable here makes sure it works with SSH too.
-  cmd = 'QPS_WORKERS="%s" bins/opt/qps_json_driver --quit' % ','.join(workers)
+  cmd = 'QPS_WORKERS="%s" bins/opt/qps_json_driver --quit' % ','.join(w.host_and_port for w in workers)
   if remote_host:
     user_at_host = '%s@%s' % (_REMOTE_HOST_USERNAME, remote_host)
     cmd = 'ssh %s "cd ~/performance_workspace/grpc/ && "%s' % (user_at_host, pipes.quote(cmd))
@@ -253,8 +259,8 @@
     sys.exit(1)
 
 
-def start_qpsworkers(languages, worker_hosts):
-  """Starts QPS workers as background jobs."""
+def create_qpsworkers(languages, worker_hosts):
+  """Creates QPS workers (but does not start them)."""
   if not worker_hosts:
     # run two workers locally (for each language)
     workers=[(None, 10000), (None, 10010)]
@@ -274,6 +280,9 @@
           for worker_idx, worker in enumerate(workers)]
 
 
+Scenario = collections.namedtuple('Scenario', 'jobspec workers name')
+
+
 def create_scenarios(languages, workers_by_lang, remote_host=None, regex='.*',
                      category='all', bq_result_table=None,
                      netperf=False, netperf_hosts=[]):
@@ -282,6 +291,7 @@
                  for workers in workers_by_lang.values()
                  for worker in workers]
   scenarios = []
+  _NO_WORKERS = []
 
   if netperf:
     if not netperf_hosts:
@@ -293,15 +303,18 @@
     else:
       netperf_server=netperf_hosts[0]
       netperf_client=netperf_hosts[1]
-    scenarios.append(create_netperf_jobspec(server_host=netperf_server,
-                                            client_host=netperf_client,
-                                            bq_result_table=bq_result_table))
+    scenarios.append(Scenario(
+        create_netperf_jobspec(server_host=netperf_server,
+                               client_host=netperf_client,
+                               bq_result_table=bq_result_table),
+        _NO_WORKERS, 'netperf'))
 
   for language in languages:
     for scenario_json in language.scenarios():
       if re.search(args.regex, scenario_json['name']):
-        if category in scenario_json.get('CATEGORIES', []) or category == 'all':
-          workers = workers_by_lang[str(language)]
+        categories = scenario_json.get('CATEGORIES', ['scalable', 'smoketest'])
+        if category in categories or category == 'all':
+          workers = workers_by_lang[str(language)][:]
           # 'SERVER_LANGUAGE' is an indicator for this script to pick
           # a server in different language.
           custom_server_lang = scenario_json.get('SERVER_LANGUAGE', None)
@@ -329,14 +342,15 @@
               # replace all client workers by workers of a different language,
               # leave num_server workers as they are server workers.
               workers[idx] = workers_by_lang[custom_client_lang][idx]
-          scenario = create_scenario_jobspec(scenario_json,
-                                             workers,
-                                             remote_host=remote_host,
-                                             bq_result_table=bq_result_table)
+          scenario = Scenario(
+              create_scenario_jobspec(scenario_json,
+                                      [w.host_and_port for w in workers],
+                                      remote_host=remote_host,
+                                      bq_result_table=bq_result_table),
+              workers,
+              scenario_json['name'])
           scenarios.append(scenario)
 
-  # the very last scenario requests shutting down the workers.
-  scenarios.append(create_quit_jobspec(all_workers, remote_host=remote_host))
   return scenarios
 
 
@@ -369,12 +383,17 @@
                   nargs='+',
                   default=[],
                   help='Worker hosts where to start QPS workers.')
+argp.add_argument('--dry_run',
+                  default=False,
+                  action='store_const',
+                  const=True,
+                  help='Just list scenarios to be run, but don\'t run them.')
 argp.add_argument('-r', '--regex', default='.*', type=str,
                   help='Regex to select scenarios to run.')
 argp.add_argument('--bq_result_table', default=None, type=str,
                   help='Bigquery "dataset.table" to upload results to.')
 argp.add_argument('--category',
-                  choices=['smoketest','all','scalable'],
+                  choices=['smoketest','all','scalable','sweep'],
                   default='all',
                   help='Select a category of tests to run.')
 argp.add_argument('--netperf',
@@ -399,53 +418,47 @@
 if args.remote_driver_host:
   remote_hosts.add(args.remote_driver_host)
 
-if remote_hosts:
-  archive_repo(languages=[str(l) for l in languages])
-  prepare_remote_hosts(remote_hosts, prepare_local=True)
-else:
-  prepare_remote_hosts([], prepare_local=True)
+if not args.dry_run:
+  if remote_hosts:
+    archive_repo(languages=[str(l) for l in languages])
+    prepare_remote_hosts(remote_hosts, prepare_local=True)
+  else:
+    prepare_remote_hosts([], prepare_local=True)
 
 build_local = False
 if not args.remote_driver_host:
   build_local = True
-build_on_remote_hosts(remote_hosts, languages=[str(l) for l in languages], build_local=build_local)
+if not args.dry_run:
+  build_on_remote_hosts(remote_hosts, languages=[str(l) for l in languages], build_local=build_local)
 
-qpsworker_jobs = start_qpsworkers(languages, args.remote_worker_host)
-
-# TODO(jtattermusch): see https://github.com/grpc/grpc/issues/6174
-time.sleep(5)
+qpsworker_jobs = create_qpsworkers(languages, args.remote_worker_host)
 
 # get list of worker addresses for each language.
-worker_addresses = dict([(str(language), []) for language in languages])
+workers_by_lang = dict([(str(language), []) for language in languages])
 for job in qpsworker_jobs:
-  worker_addresses[str(job.language)].append(job.host_and_port)
+  workers_by_lang[str(job.language)].append(job)
 
-try:
-  scenarios = create_scenarios(languages,
-                               workers_by_lang=worker_addresses,
-                               remote_host=args.remote_driver_host,
-                               regex=args.regex,
-                               category=args.category,
-                               bq_result_table=args.bq_result_table,
-                               netperf=args.netperf,
-                               netperf_hosts=args.remote_worker_host)
+scenarios = create_scenarios(languages,
+                           workers_by_lang=workers_by_lang,
+                           remote_host=args.remote_driver_host,
+                           regex=args.regex,
+                           category=args.category,
+                           bq_result_table=args.bq_result_table,
+                           netperf=args.netperf,
+                           netperf_hosts=args.remote_worker_host)
 
-  if not scenarios:
-    raise Exception('No scenarios to run')
+if not scenarios:
+  raise Exception('No scenarios to run')
 
-  jobset.message('START', 'Running scenarios.', do_newline=True)
-  num_failures, _ = jobset.run(
-      scenarios, newline_on_success=True, maxjobs=1)
-  if num_failures == 0:
-    jobset.message('SUCCESS',
-                   'All scenarios finished successfully.',
-                   do_newline=True)
+for scenario in scenarios:
+  if args.dry_run:
+    print(scenario.name)
   else:
-    jobset.message('FAILED', 'Some of the scenarios failed.',
-                   do_newline=True)
-    sys.exit(1)
-except:
-  traceback.print_exc()
-  raise
-finally:
-  finish_qps_workers(qpsworker_jobs)
+    try:
+      for worker in scenario.workers:
+        worker.start()
+      jobset.run([scenario.jobspec,
+                  create_quit_jobspec(scenario.workers, remote_host=args.remote_driver_host)],
+                 newline_on_success=True, maxjobs=1)
+    finally:
+      finish_qps_workers(scenario.workers)
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 542415d..1683316 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -139,6 +139,53 @@
   return True if os.getenv('RUN_TESTS_COMMAND') else False
 
 
+_PythonConfigVars = collections.namedtuple(
+  '_ConfigVars', ['shell', 'builder', 'builder_prefix_arguments',
+                  'venv_relative_python', 'toolchain', 'runner'])
+
+
+def _python_config_generator(name, major, minor, bits, config_vars):
+  return PythonConfig(
+    name,
+    config_vars.shell + config_vars.builder + config_vars.builder_prefix_arguments + [
+      _python_pattern_function(major=major, minor=minor, bits=bits)] + [
+      name] + config_vars.venv_relative_python + config_vars.toolchain,
+    config_vars.shell + config_vars.runner + [
+      os.path.join(name, config_vars.venv_relative_python[0])])
+
+
+def _pypy_config_generator(name, major, config_vars):
+  return PythonConfig(
+    name,
+    config_vars.shell + config_vars.builder + config_vars.builder_prefix_arguments + [
+      _pypy_pattern_function(major=major)] + [
+      name] + config_vars.venv_relative_python + config_vars.toolchain,
+    config_vars.shell + config_vars.runner + [
+      os.path.join(name, config_vars.venv_relative_python[0])])
+
+
+def _python_pattern_function(major, minor, bits):
+  # Bit-ness is handled by the test machine's environment
+  if os.name == "nt":
+    if bits == "64":
+      return '/c/Python{major}{minor}/python.exe'.format(
+        major=major, minor=minor, bits=bits)
+    else:
+      return '/c/Python{major}{minor}_{bits}bits/python.exe'.format(
+        major=major, minor=minor, bits=bits)
+  else:
+    return 'python{major}.{minor}'.format(major=major, minor=minor)
+
+
+def _pypy_pattern_function(major):
+  if major == '2':
+    return 'pypy'
+  elif major == '3':
+    return 'pypy3'
+  else:
+    raise ValueError("Unknown PyPy major version")
+
+
 class CLanguage(object):
 
   def __init__(self, make_target, test_lang):
@@ -299,7 +346,7 @@
     self.config = config
     self.args = args
     _check_compiler(self.args.compiler, ['default', 'node0.12',
-                                         'node4', 'node5'])
+                                         'node4', 'node5', 'node6'])
     if self.args.compiler == 'default':
       self.node_version = '4'
     else:
@@ -471,36 +518,40 @@
       bits = '32'
     else:
       bits = '64'
+
     if os.name == 'nt':
       shell = ['bash']
       builder = [os.path.abspath('tools/run_tests/build_python_msys2.sh')]
       builder_prefix_arguments = ['MINGW{}'.format(bits)]
       venv_relative_python = ['Scripts/python.exe']
       toolchain = ['mingw32']
-      python_pattern_function = lambda major, minor, bits: (
-          '/c/Python{major}{minor}/python.exe'.format(major=major, minor=minor, bits=bits)
-	  if bits == '64' else
-	  '/c/Python{major}{minor}_{bits}bits/python.exe'.format(
-              major=major, minor=minor, bits=bits))
     else:
       shell = []
       builder = [os.path.abspath('tools/run_tests/build_python.sh')]
       builder_prefix_arguments = []
       venv_relative_python = ['bin/python']
       toolchain = ['unix']
-      # Bit-ness is handled by the test machine's environment
-      python_pattern_function = lambda major, minor, bits: 'python{major}.{minor}'.format(major=major, minor=minor)
+
     runner = [os.path.abspath('tools/run_tests/run_python.sh')]
-    python_config_generator = lambda name, major, minor, bits: PythonConfig(
-        name,
-        shell + builder + builder_prefix_arguments
-	    + [python_pattern_function(major=major, minor=minor, bits=bits)]
-	    + [name] + venv_relative_python + toolchain,
-        shell + runner + [os.path.join(name, venv_relative_python[0])])
-    python27_config = python_config_generator(name='py27', major='2', minor='7', bits=bits)
-    python34_config = python_config_generator(name='py34', major='3', minor='4', bits=bits)
-    python35_config = python_config_generator(name='py35', major='3', minor='5', bits=bits)
-    python36_config = python_config_generator(name='py36', major='3', minor='6', bits=bits)
+    config_vars = _PythonConfigVars(shell, builder, builder_prefix_arguments,
+                              venv_relative_python, toolchain, runner)
+    python27_config = _python_config_generator(name='py27', major='2',
+                                               minor='7', bits=bits,
+                                               config_vars=config_vars)
+    python34_config = _python_config_generator(name='py34', major='3',
+                                               minor='4', bits=bits,
+                                               config_vars=config_vars)
+    python35_config = _python_config_generator(name='py35', major='3',
+                                               minor='5', bits=bits,
+                                               config_vars=config_vars)
+    python36_config = _python_config_generator(name='py36', major='3',
+                                               minor='6', bits=bits,
+                                               config_vars=config_vars)
+    pypy27_config = _pypy_config_generator(name='pypy', major='2',
+                                           config_vars=config_vars)
+    pypy32_config = _pypy_config_generator(name='pypy3', major='3',
+                                           config_vars=config_vars)
+
     if args.compiler == 'default':
       if os.name == 'nt':
         return (python27_config,)
@@ -514,6 +565,10 @@
       return (python35_config,)
     elif args.compiler == 'python3.6':
       return (python36_config,)
+    elif args.compiler == 'pypy':
+      return (pypy27_config,)
+    elif args.compiler == 'pypy3':
+      return (pypy32_config,)
     else:
       raise Exception('Compiler %s not supported.' % args.compiler)
 
@@ -599,15 +654,9 @@
     assembly_extension = '.exe'
 
     if self.args.compiler == 'coreclr':
-      if self.platform == 'linux':
-        assembly_subdir += '/netstandard1.5/debian.8-x64'
-        assembly_extension = ''
-      elif self.platform == 'mac':
-        assembly_subdir += '/netstandard1.5/osx.10.11-x64'
-        assembly_extension = ''
-      else:
-        assembly_subdir += '/netstandard1.5/win7-x64'
-      runtime_cmd = []
+      assembly_subdir += '/netcoreapp1.0'
+      runtime_cmd = ['dotnet', 'exec']
+      assembly_extension = '.dll'
     else:
       nunit_args += ['--noresult', '--workers=1']
       if self.platform == 'windows':
@@ -701,14 +750,16 @@
     _check_compiler(self.args.compiler, ['default'])
 
   def test_specs(self):
-    return [self.config.job_spec(['src/objective-c/tests/run_tests.sh'],
-                                 timeout_seconds=None,
-                                 shortname='objc-tests',
-                                 environ=_FORCE_ENVIRON_FOR_WRAPPERS),
-            self.config.job_spec(['src/objective-c/tests/build_example_test.sh'],
-                                 timeout_seconds=15*60,
-                                 shortname='objc-examples-build',
-                                 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
+    return [
+        self.config.job_spec(['src/objective-c/tests/run_tests.sh'],
+                              timeout_seconds=None,
+                              shortname='objc-tests',
+                              environ=_FORCE_ENVIRON_FOR_WRAPPERS),
+        self.config.job_spec(['src/objective-c/tests/build_example_test.sh'],
+                              timeout_seconds=30*60,
+                              shortname='objc-examples-build',
+                              environ=_FORCE_ENVIRON_FOR_WRAPPERS),
+    ]
 
   def pre_build_steps(self):
     return []
@@ -893,6 +944,7 @@
         msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)
         raise argparse.ArgumentTypeError(msg)
 
+
 # parse command line
 argp = argparse.ArgumentParser(description='Run grpc tests.')
 argp.add_argument('-c', '--config',
@@ -946,8 +998,7 @@
                            'gcc4.4', 'gcc4.6', 'gcc4.9', 'gcc5.3',
                            'clang3.4', 'clang3.5', 'clang3.6', 'clang3.7',
                            'vs2010', 'vs2013', 'vs2015',
-                           'python2.7', 'python3.4', 'python3.5', 'python3.6',
-                           'node0.12', 'node4', 'node5',
+                           'python2.7', 'python3.4', 'python3.5', 'python3.6', 'pypy', 'pypy3',
                            'coreclr'],
                   default='default',
                   help='Selects compiler to use. Allowed values depend on the platform and language.')
@@ -1146,18 +1197,6 @@
         'http://localhost:%d/quitquitquit' % legacy_server_port).read()
 
 
-def _shut_down_legacy_server(legacy_server_port):
-  try:
-    version = int(urllib2.urlopen(
-        'http://localhost:%d/version_number' % legacy_server_port,
-        timeout=10).read())
-  except:
-    pass
-  else:
-    urllib2.urlopen(
-        'http://localhost:%d/quitquitquit' % legacy_server_port).read()
-
-
 def _start_port_server(port_server_port):
   # check if a compatible port server is running
   # if incompatible (version mismatch) ==> start a new one
diff --git a/tools/run_tests/run_tests_in_workspace.sh b/tools/run_tests/run_tests_in_workspace.sh
new file mode 100755
index 0000000..98ef356
--- /dev/null
+++ b/tools/run_tests/run_tests_in_workspace.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Create a workspace in a subdirectory to allow running multiple builds in isolation.
+# WORKSPACE_NAME env variable needs to contain name of the workspace to create.
+# All cmdline args will be passed to run_tests.py script (executed in the 
+# newly created workspace)
+set -ex
+
+cd $(dirname $0)/../..
+
+rm -rf "${WORKSPACE_NAME}"
+# TODO(jtattermusch): clone --recursive fetches the submodules from github.
+# Try avoiding that to save time and network capacity.
+git clone --recursive . "${WORKSPACE_NAME}"
+
+echo "Running run_tests.py in workspace ${WORKSPACE_NAME}" 
+python "${WORKSPACE_NAME}/tools/run_tests/run_tests.py" $@
+
diff --git a/tools/run_tests/run_tests_matrix.py b/tools/run_tests/run_tests_matrix.py
new file mode 100755
index 0000000..a94f9cf
--- /dev/null
+++ b/tools/run_tests/run_tests_matrix.py
@@ -0,0 +1,282 @@
+#!/usr/bin/env python2.7
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Run test matrix."""
+
+import argparse
+import jobset
+import multiprocessing
+import os
+import report_utils
+import sys
+
+_ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
+os.chdir(_ROOT)
+
+# Set the timeout high to allow enough time for sanitizers and pre-building
+# clang docker.
+_RUNTESTS_TIMEOUT = 2*60*60
+
+# Number of jobs assigned to each run_tests.py instance
+_INNER_JOBS = 2
+
+
+def _docker_jobspec(name, runtests_args=[]):
+  """Run a single instance of run_tests.py in a docker container"""
+  test_job = jobset.JobSpec(
+          cmdline=['python', 'tools/run_tests/run_tests.py',
+                   '--use_docker',
+                   '-t',
+                   '-j', str(_INNER_JOBS),
+                   '-x', 'report_%s.xml' % name] + runtests_args,
+          shortname='run_tests_%s' % name,
+          timeout_seconds=_RUNTESTS_TIMEOUT)
+  return test_job
+
+
+def _workspace_jobspec(name, runtests_args=[], workspace_name=None):
+  """Run a single instance of run_tests.py in a separate workspace"""
+  if not workspace_name:
+    workspace_name = 'workspace_%s' % name
+  env = {'WORKSPACE_NAME': workspace_name}
+  test_job = jobset.JobSpec(
+          cmdline=['tools/run_tests/run_tests_in_workspace.sh',
+                   '-t',
+                   '-j', str(_INNER_JOBS),
+                   '-x', '../report_%s.xml' % name] + runtests_args,
+          environ=env,
+          shortname='run_tests_%s' % name,
+          timeout_seconds=_RUNTESTS_TIMEOUT)
+  return test_job
+
+
+def _generate_jobs(languages, configs, platforms,
+                  arch=None, compiler=None,
+                  labels=[], extra_args=[]):
+  result = []
+  for language in languages:
+    for platform in platforms:
+      for config in configs:
+        name = '%s_%s_%s' % (language, platform, config)
+        runtests_args = ['-l', language,
+                         '-c', config]
+        if arch or compiler:
+          name += '_%s_%s' % (arch, compiler)
+          runtests_args += ['--arch', arch,
+                            '--compiler', compiler]
+
+        runtests_args += extra_args
+        if platform == 'linux':
+          job = _docker_jobspec(name=name, runtests_args=runtests_args)
+        else:
+          job = _workspace_jobspec(name=name, runtests_args=runtests_args)
+
+        job.labels = [platform, config, language] + labels
+        result.append(job)
+  return result
+
+
+def _create_test_jobs(extra_args=[]):
+  test_jobs = []
+  # supported on linux only
+  test_jobs += _generate_jobs(languages=['sanity', 'php7'],
+                             configs=['dbg', 'opt'],
+                             platforms=['linux'],
+                             labels=['basictests'],
+                             extra_args=extra_args)
+  
+  # supported on all platforms.
+  test_jobs += _generate_jobs(languages=['c', 'csharp', 'node', 'python'],
+                             configs=['dbg', 'opt'],
+                             platforms=['linux', 'macos', 'windows'],
+                             labels=['basictests'],
+                             extra_args=extra_args)
+  
+  # supported on linux and mac.
+  test_jobs += _generate_jobs(languages=['c++', 'ruby', 'php'],
+                              configs=['dbg', 'opt'],
+                              platforms=['linux', 'macos'],
+                              labels=['basictests'],
+                              extra_args=extra_args)
+  
+  # supported on mac only.
+  test_jobs += _generate_jobs(languages=['objc'],
+                              configs=['dbg', 'opt'],
+                              platforms=['macos'],
+                              labels=['basictests'],
+                              extra_args=extra_args)
+  
+  # sanitizers
+  test_jobs += _generate_jobs(languages=['c'],
+                              configs=['msan', 'asan', 'tsan'],
+                              platforms=['linux'],
+                              labels=['sanitizers'],
+                              extra_args=extra_args)
+  test_jobs += _generate_jobs(languages=['c++'],
+                              configs=['asan', 'tsan'],
+                              platforms=['linux'],
+                              labels=['sanitizers'],
+                              extra_args=extra_args)
+  return test_jobs
+
+  
+def _create_portability_test_jobs(extra_args=[]):
+  test_jobs = []
+  # portability C x86
+  test_jobs += _generate_jobs(languages=['c'],
+                              configs=['dbg'],
+                              platforms=['linux'],
+                              arch='x86',
+                              compiler='default',
+                              labels=['portability'],
+                              extra_args=extra_args)
+  
+  # portability C and C++ on x64
+  for compiler in ['gcc4.4', 'gcc4.6', 'gcc5.3',
+                   'clang3.5', 'clang3.6', 'clang3.7']:
+    test_jobs += _generate_jobs(languages=['c', 'c++'],
+                                configs=['dbg'],
+                                platforms=['linux'],
+                                arch='x64',
+                                compiler=compiler,
+                                labels=['portability'],
+                                extra_args=extra_args)
+  
+  # portability C on Windows
+  for arch in ['x86', 'x64']:
+    for compiler in ['vs2013', 'vs2015']:
+      test_jobs += _generate_jobs(languages=['c'],
+                                  configs=['dbg'],
+                                  platforms=['windows'],
+                                  arch=arch,
+                                  compiler=compiler,
+                                  labels=['portability'],
+                                  extra_args=extra_args)
+  
+  test_jobs += _generate_jobs(languages=['python'],
+                              configs=['dbg'],
+                              platforms=['linux'],
+                              arch='default',
+                              compiler='python3.4',
+                              labels=['portability'],
+                              extra_args=extra_args)
+  
+  test_jobs += _generate_jobs(languages=['csharp'],
+                              configs=['dbg'],
+                              platforms=['linux'],
+                              arch='default',
+                              compiler='coreclr',
+                              labels=['portability'],
+                              extra_args=extra_args)
+  return test_jobs  
+
+
+def _allowed_labels():
+  """Returns a list of existing job labels."""
+  all_labels = set()
+  for job in _create_test_jobs() + _create_portability_test_jobs():
+    for label in job.labels:
+      all_labels.add(label)
+  return sorted(all_labels)
+
+
+argp = argparse.ArgumentParser(description='Run a matrix of run_tests.py tests.')
+argp.add_argument('-j', '--jobs',
+                  default=multiprocessing.cpu_count()/_INNER_JOBS,
+                  type=int,
+                  help='Number of concurrent run_tests.py instances.')
+argp.add_argument('-f', '--filter',
+                  choices=_allowed_labels(),
+                  nargs='+',
+                  default=[],
+                  help='Filter targets to run by label with AND semantics.')
+argp.add_argument('--build_only',
+                  default=False,
+                  action='store_const',
+                  const=True,
+                  help='Pass --build_only flag to run_tests.py instances.')
+argp.add_argument('--force_default_poller', default=False, action='store_const', const=True,
+                  help='Pass --force_default_poller to run_tests.py instances.')
+argp.add_argument('--dry_run',
+                  default=False,
+                  action='store_const',
+                  const=True,
+                  help='Only print what would be run.')
+args = argp.parse_args()
+
+extra_args = []
+if args.build_only:
+  extra_args.append('--build_only')
+if args.force_default_poller:
+  extra_args.append('--force_default_poller')
+
+all_jobs = _create_test_jobs(extra_args=extra_args) + _create_portability_test_jobs(extra_args=extra_args)
+
+jobs = []
+for job in all_jobs:
+  if not args.filter or all(filter in job.labels for filter in args.filter):
+    jobs.append(job)
+
+if not jobs:
+  jobset.message('FAILED', 'No test suites match given criteria.',
+                 do_newline=True)
+  sys.exit(1)
+  
+print('IMPORTANT: The changes you are testing need to be locally committed')
+print('because only the committed changes in the current branch will be')
+print('copied to the docker environment or into subworkspaces.')
+
+print 
+print 'Will run these tests:'
+for job in jobs:
+  if args.dry_run:
+    print '  %s: "%s"' % (job.shortname, ' '.join(job.cmdline))
+  else:
+    print '  %s' % job.shortname
+print
+
+if args.dry_run:
+  print '--dry_run was used, exiting'
+  sys.exit(1)
+
+jobset.message('START', 'Running test matrix.', do_newline=True)
+num_failures, resultset = jobset.run(jobs,
+                                     newline_on_success=True,
+                                     travis=True,
+                                     maxjobs=args.jobs)
+report_utils.render_junit_xml_report(resultset, 'report.xml')
+
+if num_failures == 0:
+  jobset.message('SUCCESS', 'All run_tests.py instance finished successfully.',
+                 do_newline=True)
+else:
+  jobset.message('FAILED', 'Some run_tests.py instance have failed.',
+                 do_newline=True)
+  sys.exit(1)
diff --git a/tools/run_tests/sanity/check_sources_and_headers.py b/tools/run_tests/sanity/check_sources_and_headers.py
index 28c1dc4..b733ba1 100755
--- a/tools/run_tests/sanity/check_sources_and_headers.py
+++ b/tools/run_tests/sanity/check_sources_and_headers.py
@@ -63,7 +63,8 @@
 def produces_object(name):
   return os.path.splitext(name)[1] in ['.c', '.cc']
 
-obj_producer_to_source = {'c': {}, 'c++': {}, 'csharp': {}}
+c_ish = {}
+obj_producer_to_source = {'c': c_ish, 'c++': c_ish, 'csharp': {}}
 
 errors = 0
 for target in js:
@@ -74,19 +75,19 @@
       for line in src:
         m = re_inc1.match(line)
         if m:
-          if not target_has_header(target, m.group(1)):
+          if not target_has_header(target, m.group(1)) and not target['is_filegroup']:
             print (
               'target %s (%s) does not name header %s as a dependency' % (
                 target['name'], fn, m.group(1)))
             errors += 1
         m = re_inc2.match(line)
         if m:
-          if not target_has_header(target, 'include/' + m.group(1)):
+          if not target_has_header(target, 'include/' + m.group(1)) and not target['is_filegroup']:
             print (
               'target %s (%s) does not name header %s as a dependency' % (
                 target['name'], fn, m.group(1)))
             errors += 1
-  if target['type'] == 'lib':
+  if target['type'] in ['lib', 'filegroup']:
     for fn in target['src']:
       language = target['language']
       if produces_object(fn):
diff --git a/tools/run_tests/sanity/check_submodules.sh b/tools/run_tests/sanity/check_submodules.sh
index b602d69..6410646 100755
--- a/tools/run_tests/sanity/check_submodules.sh
+++ b/tools/run_tests/sanity/check_submodules.sh
@@ -44,9 +44,9 @@
  c880e42ba1c8032d4cdde2aba0541d8a9d9fa2e9 third_party/boringssl (version_for_cocoapods_2.0-100-gc880e42)
  05b155ff59114735ec8cd089f669c4c3d8f59029 third_party/gflags (v2.1.0-45-g05b155f)
  c99458533a9b4c743ed51537e25989ea55944908 third_party/googletest (release-1.7.0)
- f8ac463766281625ad710900479130c7fcb4d63b third_party/nanopb (nanopb-0.3.4-29-gf8ac463)
- bdeb215cab2985195325fcd5e70c3fa751f46e0f third_party/protobuf (v3.0.0-beta-3.3)
+ a428e42072765993ff674fda72863c9f1aa2d268 third_party/protobuf (v3.1.0)
  50893291621658f355bc5b4d450a8d06a563053d third_party/zlib (v1.2.8)
+ bcad91771b7f0bff28a1cac1981d7ef2b9bcef3c third_party/thrift
 EOF
 
 diff -u $submodules $want_submodules
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index e85dd2c..ae5a938 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -9,6 +9,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "alarm_test", 
     "src": [
@@ -25,6 +26,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "algorithm_test", 
     "src": [
@@ -39,6 +41,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "alloc_test", 
     "src": [
@@ -55,6 +58,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "alpn_test", 
     "src": [
@@ -71,6 +75,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "api_fuzzer", 
     "src": [
@@ -88,6 +93,7 @@
       "test_tcp_server"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "bad_server_response_test", 
     "src": [
@@ -102,6 +108,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "bin_decoder_test", 
     "src": [
@@ -116,6 +123,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "bin_encoder_test", 
     "src": [
@@ -132,6 +140,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "census_context_test", 
     "src": [
@@ -148,6 +157,41 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "census_resource_test", 
+    "src": [
+      "test/core/census/resource_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "census_trace_context_test", 
+    "src": [
+      "test/core/census/trace_context_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "channel_create_test", 
     "src": [
@@ -164,6 +208,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "chttp2_hpack_encoder_test", 
     "src": [
@@ -180,6 +225,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "chttp2_status_conversion_test", 
     "src": [
@@ -196,6 +242,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "chttp2_stream_map_test", 
     "src": [
@@ -212,6 +259,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "chttp2_varint_test", 
     "src": [
@@ -228,6 +276,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "client_fuzzer", 
     "src": [
@@ -244,6 +293,24 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "combiner_test", 
+    "src": [
+      "test/core/iomgr/combiner_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "compression_test", 
     "src": [
@@ -260,6 +327,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "concurrent_connectivity_test", 
     "src": [
@@ -276,6 +344,24 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "connection_refused_test", 
+    "src": [
+      "test/core/end2end/connection_refused_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "dns_resolver_connectivity_test", 
     "src": [
@@ -292,6 +378,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "dns_resolver_test", 
     "src": [
@@ -308,6 +395,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "dualstack_socket_test", 
     "src": [
@@ -324,6 +412,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "endpoint_pair_test", 
     "src": [
@@ -340,6 +429,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "ev_epoll_linux_test", 
     "src": [
@@ -356,6 +446,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "fd_conservation_posix_test", 
     "src": [
@@ -372,6 +463,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "fd_posix_test", 
     "src": [
@@ -388,6 +480,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "fling_client", 
     "src": [
@@ -404,6 +497,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "fling_server", 
     "src": [
@@ -420,6 +514,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "fling_stream_test", 
     "src": [
@@ -436,6 +531,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "fling_test", 
     "src": [
@@ -450,6 +546,7 @@
       "grpc"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gen_hpack_tables", 
     "src": [
@@ -461,6 +558,7 @@
   {
     "deps": [], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gen_legal_metadata_characters", 
     "src": [
@@ -470,6 +568,18 @@
     "type": "target"
   }, 
   {
+    "deps": [], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "gen_percent_encoding_tables", 
+    "src": [
+      "tools/codegen/core/gen_percent_encoding_tables.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
     "deps": [
       "gpr", 
       "gpr_test_util", 
@@ -477,6 +587,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "goaway_server_test", 
     "src": [
@@ -491,6 +602,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_avl_test", 
     "src": [
@@ -505,6 +617,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_backoff_test", 
     "src": [
@@ -519,6 +632,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_cmdline_test", 
     "src": [
@@ -533,6 +647,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_cpu_test", 
     "src": [
@@ -547,6 +662,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_env_test", 
     "src": [
@@ -561,6 +677,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_histogram_test", 
     "src": [
@@ -575,6 +692,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_host_port_test", 
     "src": [
@@ -589,6 +707,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_log_test", 
     "src": [
@@ -603,6 +722,37 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "gpr_mpscq_test", 
+    "src": [
+      "test/core/support/mpscq_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "gpr_percent_encoding_test", 
+    "src": [
+      "test/core/support/percent_encoding_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_slice_buffer_test", 
     "src": [
@@ -617,6 +767,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_slice_test", 
     "src": [
@@ -631,6 +782,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_stack_lockfree_test", 
     "src": [
@@ -645,6 +797,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_string_test", 
     "src": [
@@ -659,6 +812,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_sync_test", 
     "src": [
@@ -673,6 +827,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_thd_test", 
     "src": [
@@ -687,6 +842,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_time_test", 
     "src": [
@@ -701,6 +857,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_tls_test", 
     "src": [
@@ -715,6 +872,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_useful_test", 
     "src": [
@@ -731,6 +889,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_auth_context_test", 
     "src": [
@@ -747,6 +906,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_b64_test", 
     "src": [
@@ -763,6 +923,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_byte_buffer_reader_test", 
     "src": [
@@ -779,6 +940,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_channel_args_test", 
     "src": [
@@ -795,6 +957,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_channel_stack_test", 
     "src": [
@@ -811,6 +974,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_completion_queue_test", 
     "src": [
@@ -825,6 +989,7 @@
       "grpc"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_create_jwt", 
     "src": [
@@ -841,6 +1006,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_credentials_test", 
     "src": [
@@ -857,6 +1023,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_fetch_oauth2", 
     "src": [
@@ -873,6 +1040,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_invalid_channel_args_test", 
     "src": [
@@ -889,6 +1057,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_json_token_test", 
     "src": [
@@ -905,6 +1074,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_jwt_verifier_test", 
     "src": [
@@ -919,6 +1089,7 @@
       "grpc"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_print_google_default_creds_token", 
     "src": [
@@ -935,6 +1106,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_security_connector_test", 
     "src": [
@@ -949,6 +1121,7 @@
       "grpc"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_verify_jwt", 
     "src": [
@@ -965,6 +1138,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "hpack_parser_fuzzer_test", 
     "src": [
@@ -981,6 +1155,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "hpack_parser_test", 
     "src": [
@@ -997,6 +1172,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "hpack_table_test", 
     "src": [
@@ -1013,6 +1189,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "http_parser_test", 
     "src": [
@@ -1029,6 +1206,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "http_request_fuzzer_test", 
     "src": [
@@ -1045,6 +1223,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "http_response_fuzzer_test", 
     "src": [
@@ -1061,6 +1240,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "httpcli_format_request_test", 
     "src": [
@@ -1077,6 +1257,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "httpcli_test", 
     "src": [
@@ -1093,6 +1274,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "httpscli_test", 
     "src": [
@@ -1109,6 +1291,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "init_test", 
     "src": [
@@ -1125,6 +1308,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "internal_api_canary_iomgr_test", 
     "src": [
@@ -1141,6 +1325,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "internal_api_canary_support_test", 
     "src": [
@@ -1157,6 +1342,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "internal_api_canary_transport_test", 
     "src": [
@@ -1173,6 +1359,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "invalid_call_argument_test", 
     "src": [
@@ -1189,6 +1376,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "json_fuzzer_test", 
     "src": [
@@ -1203,6 +1391,7 @@
       "grpc"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "json_rewrite", 
     "src": [
@@ -1219,6 +1408,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "json_rewrite_test", 
     "src": [
@@ -1235,6 +1425,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "json_stream_error_test", 
     "src": [
@@ -1251,6 +1442,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "json_test", 
     "src": [
@@ -1267,6 +1459,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "lame_client_test", 
     "src": [
@@ -1283,6 +1476,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "lb_policies_test", 
     "src": [
@@ -1299,6 +1493,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "load_file_test", 
     "src": [
@@ -1315,6 +1510,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "low_level_ping_pong_benchmark", 
     "src": [
@@ -1331,6 +1527,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "message_compress_test", 
     "src": [
@@ -1347,6 +1544,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "mlog_test", 
     "src": [
@@ -1363,6 +1561,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "multiple_server_queues_test", 
     "src": [
@@ -1377,6 +1576,7 @@
       "gpr_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "murmur_hash_test", 
     "src": [
@@ -1393,6 +1593,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "nanopb_fuzzer_response_test", 
     "src": [
@@ -1409,6 +1610,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "nanopb_fuzzer_serverlist_test", 
     "src": [
@@ -1425,6 +1627,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "no_server_test", 
     "src": [
@@ -1441,6 +1644,41 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer", 
+    "src": [
+      "test/core/support/percent_decode_fuzzer.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer", 
+    "src": [
+      "test/core/support/percent_encode_fuzzer.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "resolve_address_test", 
     "src": [
@@ -1457,6 +1695,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "secure_channel_create_test", 
     "src": [
@@ -1473,6 +1712,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "secure_endpoint_test", 
     "src": [
@@ -1489,6 +1729,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "sequential_connectivity_test", 
     "src": [
@@ -1505,6 +1746,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "server_chttp2_test", 
     "src": [
@@ -1521,6 +1763,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "server_fuzzer", 
     "src": [
@@ -1537,6 +1780,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "server_test", 
     "src": [
@@ -1554,6 +1798,7 @@
       "test_tcp_server"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "set_initial_connect_string_test", 
     "src": [
@@ -1570,6 +1815,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "sockaddr_resolver_test", 
     "src": [
@@ -1586,6 +1832,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "sockaddr_utils_test", 
     "src": [
@@ -1602,6 +1849,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "socket_utils_test", 
     "src": [
@@ -1618,6 +1866,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "tcp_client_posix_test", 
     "src": [
@@ -1634,6 +1883,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "tcp_posix_test", 
     "src": [
@@ -1650,6 +1900,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "tcp_server_posix_test", 
     "src": [
@@ -1666,6 +1917,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "time_averaged_stats_test", 
     "src": [
@@ -1682,10 +1934,11 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "timeout_encoding_test", 
     "src": [
-      "test/core/transport/chttp2/timeout_encoding_test.c"
+      "test/core/transport/timeout_encoding_test.c"
     ], 
     "third_party": false, 
     "type": "target"
@@ -1698,6 +1951,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "timer_heap_test", 
     "src": [
@@ -1714,6 +1968,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "timer_list_test", 
     "src": [
@@ -1730,6 +1985,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "transport_connectivity_state_test", 
     "src": [
@@ -1746,6 +2002,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "transport_metadata_test", 
     "src": [
@@ -1762,6 +2019,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "transport_security_test", 
     "src": [
@@ -1778,6 +2036,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "udp_server_test", 
     "src": [
@@ -1794,6 +2053,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "uri_fuzzer_test", 
     "src": [
@@ -1810,6 +2070,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "uri_parser_test", 
     "src": [
@@ -1828,6 +2089,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "alarm_cpp_test", 
     "src": [
@@ -1846,6 +2108,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "async_end2end_test", 
     "src": [
@@ -1864,6 +2127,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "auth_property_iterator_test", 
     "src": [
@@ -1879,6 +2143,7 @@
       "grpc++"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "channel_arguments_test", 
     "src": [
@@ -1898,6 +2163,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "cli_call_test", 
     "src": [
@@ -1916,6 +2182,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "client_crash_test", 
     "src": [
@@ -1934,6 +2201,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "client_crash_test_server", 
     "src": [
@@ -1961,6 +2229,7 @@
       "src/proto/grpc/testing/stats.grpc.pb.h", 
       "src/proto/grpc/testing/stats.pb.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "codegen_test_full", 
     "src": [
@@ -1986,6 +2255,7 @@
       "src/proto/grpc/testing/stats.grpc.pb.h", 
       "src/proto/grpc/testing/stats.pb.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "codegen_test_minimal", 
     "src": [
@@ -2001,6 +2271,7 @@
       "grpc++"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "credentials_test", 
     "src": [
@@ -2018,6 +2289,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "cxx_byte_buffer_test", 
     "src": [
@@ -2035,6 +2307,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "cxx_slice_test", 
     "src": [
@@ -2048,6 +2321,7 @@
       "grpc++"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "cxx_string_ref_test", 
     "src": [
@@ -2065,6 +2339,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "cxx_time_test", 
     "src": [
@@ -2083,6 +2358,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "end2end_test", 
     "src": [
@@ -2101,6 +2377,26 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "filter_end2end_test", 
+    "src": [
+      "test/cpp/end2end/filter_end2end_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_test_util", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "generic_end2end_test", 
     "src": [
@@ -2119,6 +2415,7 @@
       "src/proto/grpc/testing/compiler_test.grpc.pb.h", 
       "src/proto/grpc/testing/compiler_test.pb.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "golden_file_test", 
     "src": [
@@ -2130,16 +2427,14 @@
   {
     "deps": [
       "gpr", 
-      "gpr_test_util", 
       "grpc", 
       "grpc++", 
       "grpc++_reflection", 
       "grpc++_test_config", 
-      "grpc++_test_util", 
-      "grpc_cli_libs", 
-      "grpc_test_util"
+      "grpc_cli_libs"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc_cli", 
     "src": [
@@ -2153,6 +2448,7 @@
       "grpc_plugin_support"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc_cpp_plugin", 
     "src": [
@@ -2166,6 +2462,7 @@
       "grpc_plugin_support"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc_csharp_plugin", 
     "src": [
@@ -2179,6 +2476,7 @@
       "grpc_plugin_support"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc_node_plugin", 
     "src": [
@@ -2192,6 +2490,7 @@
       "grpc_plugin_support"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc_objective_c_plugin", 
     "src": [
@@ -2205,6 +2504,21 @@
       "grpc_plugin_support"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "grpc_php_plugin", 
+    "src": [
+      "src/compiler/php_plugin.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "grpc_plugin_support"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc_python_plugin", 
     "src": [
@@ -2218,6 +2532,7 @@
       "grpc_plugin_support"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc_ruby_plugin", 
     "src": [
@@ -2228,6 +2543,36 @@
   }, 
   {
     "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_codegen_proto", 
+      "grpc++_config_proto", 
+      "grpc++_reflection", 
+      "grpc_cli_libs", 
+      "grpc_test_util"
+    ], 
+    "headers": [
+      "src/proto/grpc/testing/echo.grpc.pb.h", 
+      "src/proto/grpc/testing/echo.pb.h", 
+      "src/proto/grpc/testing/echo_messages.grpc.pb.h", 
+      "src/proto/grpc/testing/echo_messages.pb.h", 
+      "test/cpp/util/string_ref_helper.h"
+    ], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "grpc_tool_test", 
+    "src": [
+      "test/cpp/util/grpc_tool_test.cc", 
+      "test/cpp/util/string_ref_helper.cc", 
+      "test/cpp/util/string_ref_helper.h"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
       "grpc", 
       "grpc++", 
       "grpc++_test_util", 
@@ -2237,6 +2582,7 @@
       "src/proto/grpc/lb/v1/load_balancer.grpc.pb.h", 
       "src/proto/grpc/lb/v1/load_balancer.pb.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpclb_api_test", 
     "src": [
@@ -2254,7 +2600,30 @@
       "grpc++_test_util", 
       "grpc_test_util"
     ], 
+    "headers": [
+      "src/proto/grpc/lb/v1/load_balancer.grpc.pb.h", 
+      "src/proto/grpc/lb/v1/load_balancer.pb.h"
+    ], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "grpclb_test", 
+    "src": [
+      "test/cpp/grpclb/grpclb_test.cc"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc++", 
+      "grpc++_test_util", 
+      "grpc_test_util"
+    ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "hybrid_end2end_test", 
     "src": [
@@ -2276,6 +2645,7 @@
       "interop_client_main"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "interop_client", 
     "src": [], 
@@ -2292,9 +2662,11 @@
       "grpc++_test_util", 
       "grpc_test_util", 
       "interop_server_helper", 
+      "interop_server_lib", 
       "interop_server_main"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "interop_server", 
     "src": [], 
@@ -2309,6 +2681,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "interop_test", 
     "src": [
@@ -2328,6 +2701,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "json_run_localhost", 
     "src": [
@@ -2348,6 +2722,7 @@
       "src/proto/grpc/testing/metrics.pb.h", 
       "test/cpp/util/metrics_server.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "metrics_client", 
     "src": [
@@ -2367,6 +2742,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "mock_test", 
     "src": [
@@ -2388,6 +2764,7 @@
     "headers": [
       "test/cpp/util/proto_reflection_descriptor_database.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "proto_server_reflection_test", 
     "src": [
@@ -2409,6 +2786,7 @@
       "qps"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "qps_interarrival_test", 
     "src": [
@@ -2429,6 +2807,7 @@
       "qps"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "qps_json_driver", 
     "src": [
@@ -2449,6 +2828,7 @@
       "qps"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "qps_openloop_test", 
     "src": [
@@ -2472,6 +2852,7 @@
       "test/cpp/qps/client.h", 
       "test/cpp/qps/server.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "qps_worker", 
     "src": [
@@ -2500,6 +2881,7 @@
       "src/proto/grpc/testing/test.grpc.pb.h", 
       "src/proto/grpc/testing/test.pb.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "reconnect_interop_client", 
     "src": [
@@ -2528,6 +2910,7 @@
       "src/proto/grpc/testing/test.grpc.pb.h", 
       "src/proto/grpc/testing/test.pb.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "reconnect_interop_server", 
     "src": [
@@ -2546,6 +2929,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "secure_auth_context_test", 
     "src": [
@@ -2565,6 +2949,7 @@
       "qps"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "secure_sync_unary_ping_pong_test", 
     "src": [
@@ -2583,6 +2968,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "server_builder_plugin_test", 
     "src": [
@@ -2601,6 +2987,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "server_crash_test", 
     "src": [
@@ -2619,6 +3006,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "server_crash_test_client", 
     "src": [
@@ -2637,6 +3025,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "shutdown_test", 
     "src": [
@@ -2654,6 +3043,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "status_test", 
     "src": [
@@ -2672,6 +3062,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "streaming_throughput_test", 
     "src": [
@@ -2704,6 +3095,7 @@
       "test/cpp/interop/stress_interop_client.h", 
       "test/cpp/util/metrics_server.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "stress_test", 
     "src": [
@@ -2729,6 +3121,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "thread_stress_test", 
     "src": [
@@ -2743,6 +3136,7 @@
       "grpc"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c89", 
     "name": "public_headers_must_be_c89", 
     "src": [
@@ -2758,6 +3152,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_aes_test", 
     "src": [], 
@@ -2771,6 +3166,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_asn1_test", 
     "src": [], 
@@ -2784,6 +3180,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_base64_test", 
     "src": [], 
@@ -2797,6 +3194,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_bio_test", 
     "src": [], 
@@ -2810,6 +3208,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_bn_test", 
     "src": [], 
@@ -2823,6 +3222,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_bytestring_test", 
     "src": [], 
@@ -2836,6 +3236,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_aead_test", 
     "src": [], 
@@ -2849,6 +3250,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_cipher_test", 
     "src": [], 
@@ -2862,6 +3264,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_cmac_test", 
     "src": [], 
@@ -2875,6 +3278,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_constant_time_test", 
     "src": [], 
@@ -2888,6 +3292,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_ed25519_test", 
     "src": [], 
@@ -2901,6 +3306,7 @@
       "boringssl_x25519_test_lib"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_x25519_test", 
     "src": [], 
@@ -2914,6 +3320,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_dh_test", 
     "src": [], 
@@ -2927,6 +3334,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_digest_test", 
     "src": [], 
@@ -2940,6 +3348,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_dsa_test", 
     "src": [], 
@@ -2953,6 +3362,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_ec_test", 
     "src": [], 
@@ -2966,6 +3376,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_example_mul", 
     "src": [], 
@@ -2979,6 +3390,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_ecdsa_test", 
     "src": [], 
@@ -2992,6 +3404,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_err_test", 
     "src": [], 
@@ -3005,6 +3418,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_evp_extra_test", 
     "src": [], 
@@ -3018,6 +3432,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_evp_test", 
     "src": [], 
@@ -3031,6 +3446,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_pbkdf_test", 
     "src": [], 
@@ -3044,6 +3460,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_hkdf_test", 
     "src": [], 
@@ -3057,6 +3474,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_hmac_test", 
     "src": [], 
@@ -3070,6 +3488,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_lhash_test", 
     "src": [], 
@@ -3083,6 +3502,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_gcm_test", 
     "src": [], 
@@ -3096,6 +3516,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_pkcs12_test", 
     "src": [], 
@@ -3109,6 +3530,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_pkcs8_test", 
     "src": [], 
@@ -3122,6 +3544,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_poly1305_test", 
     "src": [], 
@@ -3135,6 +3558,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_refcount_test", 
     "src": [], 
@@ -3148,6 +3572,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_rsa_test", 
     "src": [], 
@@ -3161,6 +3586,7 @@
       "boringssl_thread_test_lib"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_thread_test", 
     "src": [], 
@@ -3174,6 +3600,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_pkcs7_test", 
     "src": [], 
@@ -3187,6 +3614,7 @@
       "boringssl_x509_test_lib"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_x509_test", 
     "src": [], 
@@ -3200,6 +3628,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_tab_test", 
     "src": [], 
@@ -3213,6 +3642,7 @@
       "boringssl_v3name_test_lib"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_v3name_test", 
     "src": [], 
@@ -3226,6 +3656,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_pqueue_test", 
     "src": [], 
@@ -3239,6 +3670,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_ssl_test", 
     "src": [], 
@@ -3254,6 +3686,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "badreq_bad_client_test", 
     "src": [
@@ -3271,6 +3704,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "connection_prefix_bad_client_test", 
     "src": [
@@ -3288,6 +3722,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "head_of_line_blocking_bad_client_test", 
     "src": [
@@ -3305,6 +3740,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "headers_bad_client_test", 
     "src": [
@@ -3322,6 +3758,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "initial_settings_frame_bad_client_test", 
     "src": [
@@ -3339,6 +3776,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "large_metadata_bad_client_test", 
     "src": [
@@ -3356,6 +3794,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "server_registered_method_bad_client_test", 
     "src": [
@@ -3373,6 +3812,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "simple_request_bad_client_test", 
     "src": [
@@ -3390,6 +3830,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "unknown_frame_bad_client_test", 
     "src": [
@@ -3407,6 +3848,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "window_overflow_bad_client_test", 
     "src": [
@@ -3424,23 +3866,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
-    "language": "c", 
-    "name": "bad_ssl_alpn_server", 
-    "src": [
-      "test/core/bad_ssl/servers/alpn.c"
-    ], 
-    "third_party": false, 
-    "type": "target"
-  }, 
-  {
-    "deps": [
-      "bad_ssl_test_server", 
-      "gpr", 
-      "gpr_test_util", 
-      "grpc", 
-      "grpc_test_util"
-    ], 
-    "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "bad_ssl_cert_server", 
     "src": [
@@ -3457,22 +3883,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
-    "language": "c", 
-    "name": "bad_ssl_alpn_test", 
-    "src": [
-      "test/core/bad_ssl/bad_ssl_test.c"
-    ], 
-    "third_party": false, 
-    "type": "target"
-  }, 
-  {
-    "deps": [
-      "gpr", 
-      "gpr_test_util", 
-      "grpc", 
-      "grpc_test_util"
-    ], 
-    "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "bad_ssl_cert_test", 
     "src": [
@@ -3490,6 +3901,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_census_test", 
     "src": [
@@ -3507,6 +3919,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_compress_test", 
     "src": [
@@ -3524,6 +3937,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_fakesec_test", 
     "src": [
@@ -3541,6 +3955,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_fd_test", 
     "src": [
@@ -3558,6 +3973,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_full_test", 
     "src": [
@@ -3575,6 +3991,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_full+pipe_test", 
     "src": [
@@ -3592,6 +4009,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_full+trace_test", 
     "src": [
@@ -3609,10 +4027,11 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_http_proxy_test", 
     "src": [
-      "test/core/end2end/fixtures/h2_loadreporting.c"
+      "test/core/end2end/fixtures/h2_http_proxy.c"
     ], 
     "third_party": false, 
     "type": "target"
@@ -3626,6 +4045,25 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
+    "src": [
+      "test/core/end2end/fixtures/h2_load_reporting.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "end2end_tests", 
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_oauth2_test", 
     "src": [
@@ -3643,6 +4081,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_proxy_test", 
     "src": [
@@ -3660,6 +4099,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_sockpair_test", 
     "src": [
@@ -3677,6 +4117,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_sockpair+trace_test", 
     "src": [
@@ -3694,6 +4135,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_sockpair_1byte_test", 
     "src": [
@@ -3711,6 +4153,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_ssl_test", 
     "src": [
@@ -3728,6 +4171,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_ssl_cert_test", 
     "src": [
@@ -3745,6 +4189,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_ssl_proxy_test", 
     "src": [
@@ -3762,6 +4207,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_uds_test", 
     "src": [
@@ -3779,6 +4225,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_census_nosec_test", 
     "src": [
@@ -3796,6 +4243,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_compress_nosec_test", 
     "src": [
@@ -3813,6 +4261,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_fd_nosec_test", 
     "src": [
@@ -3830,6 +4279,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_full_nosec_test", 
     "src": [
@@ -3847,6 +4297,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_full+pipe_nosec_test", 
     "src": [
@@ -3864,6 +4315,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_full+trace_nosec_test", 
     "src": [
@@ -3881,10 +4333,11 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_http_proxy_nosec_test", 
     "src": [
-      "test/core/end2end/fixtures/h2_loadreporting.c"
+      "test/core/end2end/fixtures/h2_http_proxy.c"
     ], 
     "third_party": false, 
     "type": "target"
@@ -3898,6 +4351,25 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
+    "src": [
+      "test/core/end2end/fixtures/h2_load_reporting.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "end2end_nosec_tests", 
+      "gpr", 
+      "gpr_test_util", 
+      "grpc_test_util_unsecure", 
+      "grpc_unsecure"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_proxy_nosec_test", 
     "src": [
@@ -3915,6 +4387,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_sockpair_nosec_test", 
     "src": [
@@ -3932,6 +4405,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_sockpair+trace_nosec_test", 
     "src": [
@@ -3949,6 +4423,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_sockpair_1byte_nosec_test", 
     "src": [
@@ -3966,6 +4441,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "h2_uds_nosec_test", 
     "src": [
@@ -3982,6 +4458,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "api_fuzzer_one_entry", 
     "src": [
@@ -3999,6 +4476,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "client_fuzzer_one_entry", 
     "src": [
@@ -4016,6 +4494,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "hpack_parser_fuzzer_test_one_entry", 
     "src": [
@@ -4033,6 +4512,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "http_request_fuzzer_test_one_entry", 
     "src": [
@@ -4050,6 +4530,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "http_response_fuzzer_test_one_entry", 
     "src": [
@@ -4067,6 +4548,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "json_fuzzer_test_one_entry", 
     "src": [
@@ -4084,6 +4566,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "nanopb_fuzzer_response_test_one_entry", 
     "src": [
@@ -4101,6 +4584,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "nanopb_fuzzer_serverlist_test_one_entry", 
     "src": [
@@ -4118,6 +4602,43 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "src": [
+      "test/core/support/percent_decode_fuzzer.c", 
+      "test/core/util/one_corpus_entry_fuzzer.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "src": [
+      "test/core/support/percent_encode_fuzzer.c", 
+      "test/core/util/one_corpus_entry_fuzzer.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "server_fuzzer_one_entry", 
     "src": [
@@ -4135,6 +4656,7 @@
       "grpc_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "uri_fuzzer_test_one_entry", 
     "src": [
@@ -4149,6 +4671,7 @@
       "gpr_base"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr", 
     "src": [], 
@@ -4162,6 +4685,7 @@
     "headers": [
       "test/core/util/test_config.h"
     ], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "gpr_test_util", 
     "src": [
@@ -4189,6 +4713,7 @@
       "grpc_transport_chttp2_server_secure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc", 
     "src": [
@@ -4205,6 +4730,7 @@
       "grpc_transport_cronet_client_secure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_cronet", 
     "src": [
@@ -4219,6 +4745,7 @@
       "grpc"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_dll", 
     "src": [], 
@@ -4230,12 +4757,14 @@
       "gpr", 
       "gpr_test_util", 
       "grpc", 
+      "grpc_base", 
       "grpc_test_util_base"
     ], 
     "headers": [
       "test/core/end2end/data/ssl_test_data.h", 
       "test/core/security/oauth2_utils.h"
     ], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_test_util", 
     "src": [
@@ -4259,6 +4788,7 @@
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_test_util_unsecure", 
     "src": [], 
@@ -4280,6 +4810,7 @@
       "grpc_transport_chttp2_server_insecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "grpc_unsecure", 
     "src": [
@@ -4300,6 +4831,7 @@
     "headers": [
       "test/core/util/reconnect_server.h"
     ], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "reconnect_server", 
     "src": [
@@ -4319,6 +4851,7 @@
     "headers": [
       "test/core/util/test_tcp_server.h"
     ], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "test_tcp_server", 
     "src": [
@@ -4341,10 +4874,12 @@
       "src/cpp/common/secure_auth_context.h", 
       "src/cpp/server/secure_server_credentials.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc++", 
     "src": [
       "include/grpc++/impl/codegen/core_codegen.h", 
+      "src/cpp/client/insecure_credentials.cc", 
       "src/cpp/client/secure_credentials.cc", 
       "src/cpp/client/secure_credentials.h", 
       "src/cpp/common/auth_property_iterator.cc", 
@@ -4352,6 +4887,7 @@
       "src/cpp/common/secure_auth_context.h", 
       "src/cpp/common/secure_channel_arguments.cc", 
       "src/cpp/common/secure_create_auth_context.cc", 
+      "src/cpp/server/insecure_server_credentials.cc", 
       "src/cpp/server/secure_server_credentials.cc", 
       "src/cpp/server/secure_server_credentials.h"
     ], 
@@ -4367,6 +4903,7 @@
       "include/grpc++/ext/proto_server_reflection_plugin.h", 
       "src/cpp/ext/proto_server_reflection.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc++_reflection", 
     "src": [
@@ -4384,6 +4921,7 @@
       "src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.h", 
       "src/proto/grpc/reflection/v1alpha/reflection.pb.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc++_reflection_codegen", 
     "src": [], 
@@ -4395,11 +4933,12 @@
     "headers": [
       "test/cpp/util/test_config.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc++_test_config", 
     "src": [
-      "test/cpp/util/test_config.cc", 
-      "test/cpp/util/test_config.h"
+      "test/cpp/util/test_config.h", 
+      "test/cpp/util/test_config_cc.cc"
     ], 
     "third_party": false, 
     "type": "lib"
@@ -4411,7 +4950,8 @@
       "grpc++_codegen_base_src", 
       "grpc++_codegen_proto", 
       "grpc++_config_proto", 
-      "grpc_test_util"
+      "grpc_test_util", 
+      "thrift_util"
     ], 
     "headers": [
       "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h", 
@@ -4427,6 +4967,7 @@
       "test/cpp/util/subprocess.h", 
       "test/cpp/util/test_credentials_provider.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc++_test_util", 
     "src": [
@@ -4449,17 +4990,19 @@
   {
     "deps": [
       "gpr", 
-      "grpc", 
       "grpc++_base", 
       "grpc++_codegen_base", 
       "grpc++_codegen_base_src", 
       "grpc_unsecure"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc++_unsecure", 
     "src": [
-      "src/cpp/common/insecure_create_auth_context.cc"
+      "src/cpp/client/insecure_credentials.cc", 
+      "src/cpp/common/insecure_create_auth_context.cc", 
+      "src/cpp/server/insecure_server_credentials.cc"
     ], 
     "third_party": false, 
     "type": "lib"
@@ -4468,22 +5011,34 @@
     "deps": [
       "grpc++", 
       "grpc++_reflection", 
-      "grpc_plugin_support"
+      "grpc++_test_config"
     ], 
     "headers": [
       "test/cpp/util/cli_call.h", 
+      "test/cpp/util/cli_credentials.h", 
+      "test/cpp/util/config_grpc_cli.h", 
+      "test/cpp/util/grpc_tool.h", 
       "test/cpp/util/proto_file_parser.h", 
-      "test/cpp/util/proto_reflection_descriptor_database.h"
+      "test/cpp/util/proto_reflection_descriptor_database.h", 
+      "test/cpp/util/service_describer.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc_cli_libs", 
     "src": [
       "test/cpp/util/cli_call.cc", 
       "test/cpp/util/cli_call.h", 
+      "test/cpp/util/cli_credentials.cc", 
+      "test/cpp/util/cli_credentials.h", 
+      "test/cpp/util/config_grpc_cli.h", 
+      "test/cpp/util/grpc_tool.cc", 
+      "test/cpp/util/grpc_tool.h", 
       "test/cpp/util/proto_file_parser.cc", 
       "test/cpp/util/proto_file_parser.h", 
       "test/cpp/util/proto_reflection_descriptor_database.cc", 
-      "test/cpp/util/proto_reflection_descriptor_database.h"
+      "test/cpp/util/proto_reflection_descriptor_database.h", 
+      "test/cpp/util/service_describer.cc", 
+      "test/cpp/util/service_describer.h"
     ], 
     "third_party": false, 
     "type": "lib"
@@ -4503,12 +5058,15 @@
       "src/compiler/node_generator_helpers.h", 
       "src/compiler/objective_c_generator.h", 
       "src/compiler/objective_c_generator_helpers.h", 
+      "src/compiler/php_generator.h", 
+      "src/compiler/php_generator_helpers.h", 
       "src/compiler/python_generator.h", 
       "src/compiler/ruby_generator.h", 
       "src/compiler/ruby_generator_helpers-inl.h", 
       "src/compiler/ruby_generator_map-inl.h", 
       "src/compiler/ruby_generator_string-inl.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "grpc_plugin_support", 
     "src": [
@@ -4526,6 +5084,9 @@
       "src/compiler/objective_c_generator.cc", 
       "src/compiler/objective_c_generator.h", 
       "src/compiler/objective_c_generator_helpers.h", 
+      "src/compiler/php_generator.cc", 
+      "src/compiler/php_generator.h", 
+      "src/compiler/php_generator_helpers.h", 
       "src/compiler/python_generator.cc", 
       "src/compiler/python_generator.h", 
       "src/compiler/ruby_generator.cc", 
@@ -4550,6 +5111,7 @@
       "src/proto/grpc/testing/messages.pb.h", 
       "test/cpp/interop/client_helper.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "interop_client_helper", 
     "src": [
@@ -4579,6 +5141,7 @@
       "src/proto/grpc/testing/test.pb.h", 
       "test/cpp/interop/interop_client.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "interop_client_main", 
     "src": [
@@ -4599,6 +5162,7 @@
     "headers": [
       "test/cpp/interop/server_helper.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "interop_server_helper", 
     "src": [
@@ -4627,10 +5191,25 @@
       "src/proto/grpc/testing/test.grpc.pb.h", 
       "src/proto/grpc/testing/test.pb.h"
     ], 
+    "is_filegroup": false, 
+    "language": "c++", 
+    "name": "interop_server_lib", 
+    "src": [
+      "test/cpp/interop/interop_server.cc"
+    ], 
+    "third_party": false, 
+    "type": "lib"
+  }, 
+  {
+    "deps": [
+      "interop_server_lib"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "interop_server_main", 
     "src": [
-      "test/cpp/interop/interop_server.cc"
+      "test/cpp/interop/interop_server_bootstrap.cc"
     ], 
     "third_party": false, 
     "type": "lib"
@@ -4665,6 +5244,7 @@
       "test/cpp/qps/usage_timer.h", 
       "test/cpp/util/benchmark_config.h"
     ], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "qps", 
     "src": [
@@ -4701,6 +5281,7 @@
       "grpc"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "csharp", 
     "name": "grpc_csharp_ext", 
     "src": [
@@ -4820,6 +5401,7 @@
       "third_party/boringssl/ssl/test/scoped_types.h", 
       "third_party/boringssl/ssl/test/test_config.h"
     ], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl", 
     "src": [
@@ -4831,6 +5413,7 @@
   {
     "deps": [], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_test_util", 
     "src": [], 
@@ -4843,6 +5426,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_aes_test_lib", 
     "src": [], 
@@ -4855,6 +5439,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_asn1_test_lib", 
     "src": [], 
@@ -4867,6 +5452,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_base64_test_lib", 
     "src": [], 
@@ -4879,6 +5465,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_bio_test_lib", 
     "src": [], 
@@ -4891,6 +5478,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_bn_test_lib", 
     "src": [], 
@@ -4903,6 +5491,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_bytestring_test_lib", 
     "src": [], 
@@ -4915,6 +5504,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_aead_test_lib", 
     "src": [], 
@@ -4927,6 +5517,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_cipher_test_lib", 
     "src": [], 
@@ -4939,6 +5530,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_cmac_test_lib", 
     "src": [], 
@@ -4951,6 +5543,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl_constant_time_test_lib", 
     "src": [], 
@@ -4963,6 +5556,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_ed25519_test_lib", 
     "src": [], 
@@ -4975,6 +5569,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_x25519_test_lib", 
     "src": [], 
@@ -4987,6 +5582,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_dh_test_lib", 
     "src": [], 
@@ -4999,6 +5595,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_digest_test_lib", 
     "src": [], 
@@ -5011,6 +5608,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl_dsa_test_lib", 
     "src": [], 
@@ -5023,6 +5621,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_ec_test_lib", 
     "src": [], 
@@ -5035,6 +5634,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl_example_mul_lib", 
     "src": [], 
@@ -5047,6 +5647,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_ecdsa_test_lib", 
     "src": [], 
@@ -5059,6 +5660,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_err_test_lib", 
     "src": [], 
@@ -5071,6 +5673,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_evp_extra_test_lib", 
     "src": [], 
@@ -5083,6 +5686,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_evp_test_lib", 
     "src": [], 
@@ -5095,6 +5699,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_pbkdf_test_lib", 
     "src": [], 
@@ -5107,6 +5712,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl_hkdf_test_lib", 
     "src": [], 
@@ -5119,6 +5725,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_hmac_test_lib", 
     "src": [], 
@@ -5131,6 +5738,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl_lhash_test_lib", 
     "src": [], 
@@ -5143,6 +5751,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl_gcm_test_lib", 
     "src": [], 
@@ -5155,6 +5764,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_pkcs12_test_lib", 
     "src": [], 
@@ -5167,6 +5777,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_pkcs8_test_lib", 
     "src": [], 
@@ -5179,6 +5790,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_poly1305_test_lib", 
     "src": [], 
@@ -5191,6 +5803,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl_refcount_test_lib", 
     "src": [], 
@@ -5203,6 +5816,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_rsa_test_lib", 
     "src": [], 
@@ -5215,6 +5829,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl_thread_test_lib", 
     "src": [], 
@@ -5227,6 +5842,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl_pkcs7_test_lib", 
     "src": [], 
@@ -5239,6 +5855,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_x509_test_lib", 
     "src": [], 
@@ -5251,6 +5868,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl_tab_test_lib", 
     "src": [], 
@@ -5263,6 +5881,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl_v3name_test_lib", 
     "src": [], 
@@ -5275,6 +5894,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "boringssl_pqueue_test_lib", 
     "src": [], 
@@ -5287,6 +5907,7 @@
       "boringssl_test_util"
     ], 
     "headers": [], 
+    "is_filegroup": false, 
     "language": "c++", 
     "name": "boringssl_ssl_test_lib", 
     "src": [], 
@@ -5308,6 +5929,7 @@
       "third_party/zlib/zlib.h", 
       "third_party/zlib/zutil.h"
     ], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "z", 
     "src": [], 
@@ -5324,6 +5946,7 @@
     "headers": [
       "test/core/bad_client/bad_client.h"
     ], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "bad_client_test", 
     "src": [
@@ -5343,6 +5966,7 @@
     "headers": [
       "test/core/bad_ssl/server_common.h"
     ], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "bad_ssl_test_server", 
     "src": [
@@ -5363,6 +5987,7 @@
       "test/core/end2end/end2end_tests.h", 
       "test/core/end2end/tests/cancel_test_helpers.h"
     ], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "end2end_tests", 
     "src": [
@@ -5383,6 +6008,7 @@
       "test/core/end2end/tests/default_host.c", 
       "test/core/end2end/tests/disappearing_server.c", 
       "test/core/end2end/tests/empty_batch.c", 
+      "test/core/end2end/tests/filter_call_init_fails.c", 
       "test/core/end2end/tests/filter_causes_close.c", 
       "test/core/end2end/tests/graceful_server_shutdown.c", 
       "test/core/end2end/tests/high_initial_seqno.c", 
@@ -5390,10 +6016,12 @@
       "test/core/end2end/tests/idempotent_request.c", 
       "test/core/end2end/tests/invoke_large_request.c", 
       "test/core/end2end/tests/large_metadata.c", 
+      "test/core/end2end/tests/load_reporting_hook.c", 
       "test/core/end2end/tests/max_concurrent_streams.c", 
       "test/core/end2end/tests/max_message_length.c", 
       "test/core/end2end/tests/negative_deadline.c", 
       "test/core/end2end/tests/network_status_change.c", 
+      "test/core/end2end/tests/no_logging.c", 
       "test/core/end2end/tests/no_op.c", 
       "test/core/end2end/tests/payload.c", 
       "test/core/end2end/tests/ping.c", 
@@ -5404,6 +6032,7 @@
       "test/core/end2end/tests/server_finishes_request.c", 
       "test/core/end2end/tests/shutdown_finishes_calls.c", 
       "test/core/end2end/tests/shutdown_finishes_tags.c", 
+      "test/core/end2end/tests/simple_cacheable_request.c", 
       "test/core/end2end/tests/simple_delayed_request.c", 
       "test/core/end2end/tests/simple_metadata.c", 
       "test/core/end2end/tests/simple_request.c", 
@@ -5424,6 +6053,7 @@
       "test/core/end2end/end2end_tests.h", 
       "test/core/end2end/tests/cancel_test_helpers.h"
     ], 
+    "is_filegroup": false, 
     "language": "c", 
     "name": "end2end_nosec_tests", 
     "src": [
@@ -5443,6 +6073,7 @@
       "test/core/end2end/tests/default_host.c", 
       "test/core/end2end/tests/disappearing_server.c", 
       "test/core/end2end/tests/empty_batch.c", 
+      "test/core/end2end/tests/filter_call_init_fails.c", 
       "test/core/end2end/tests/filter_causes_close.c", 
       "test/core/end2end/tests/graceful_server_shutdown.c", 
       "test/core/end2end/tests/high_initial_seqno.c", 
@@ -5450,10 +6081,12 @@
       "test/core/end2end/tests/idempotent_request.c", 
       "test/core/end2end/tests/invoke_large_request.c", 
       "test/core/end2end/tests/large_metadata.c", 
+      "test/core/end2end/tests/load_reporting_hook.c", 
       "test/core/end2end/tests/max_concurrent_streams.c", 
       "test/core/end2end/tests/max_message_length.c", 
       "test/core/end2end/tests/negative_deadline.c", 
       "test/core/end2end/tests/network_status_change.c", 
+      "test/core/end2end/tests/no_logging.c", 
       "test/core/end2end/tests/no_op.c", 
       "test/core/end2end/tests/payload.c", 
       "test/core/end2end/tests/ping.c", 
@@ -5464,6 +6097,7 @@
       "test/core/end2end/tests/server_finishes_request.c", 
       "test/core/end2end/tests/shutdown_finishes_calls.c", 
       "test/core/end2end/tests/shutdown_finishes_tags.c", 
+      "test/core/end2end/tests/simple_cacheable_request.c", 
       "test/core/end2end/tests/simple_delayed_request.c", 
       "test/core/end2end/tests/simple_metadata.c", 
       "test/core/end2end/tests/simple_request.c", 
@@ -5482,23 +6116,32 @@
     "headers": [
       "include/grpc/census.h", 
       "src/core/ext/census/aggregation.h", 
+      "src/core/ext/census/base_resources.h", 
       "src/core/ext/census/census_interface.h", 
       "src/core/ext/census/census_rpc_stats.h", 
       "src/core/ext/census/gen/census.pb.h", 
+      "src/core/ext/census/gen/trace_context.pb.h", 
       "src/core/ext/census/grpc_filter.h", 
       "src/core/ext/census/mlog.h", 
-      "src/core/ext/census/rpc_metric_id.h"
+      "src/core/ext/census/resource.h", 
+      "src/core/ext/census/rpc_metric_id.h", 
+      "src/core/ext/census/trace_context.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "census", 
     "src": [
       "include/grpc/census.h", 
       "src/core/ext/census/aggregation.h", 
+      "src/core/ext/census/base_resources.c", 
+      "src/core/ext/census/base_resources.h", 
       "src/core/ext/census/census_interface.h", 
       "src/core/ext/census/census_rpc_stats.h", 
       "src/core/ext/census/context.c", 
       "src/core/ext/census/gen/census.pb.c", 
       "src/core/ext/census/gen/census.pb.h", 
+      "src/core/ext/census/gen/trace_context.pb.c", 
+      "src/core/ext/census/gen/trace_context.pb.h", 
       "src/core/ext/census/grpc_context.c", 
       "src/core/ext/census/grpc_filter.c", 
       "src/core/ext/census/grpc_filter.h", 
@@ -5508,7 +6151,11 @@
       "src/core/ext/census/mlog.h", 
       "src/core/ext/census/operation.c", 
       "src/core/ext/census/placeholders.c", 
+      "src/core/ext/census/resource.c", 
+      "src/core/ext/census/resource.h", 
       "src/core/ext/census/rpc_metric_id.h", 
+      "src/core/ext/census/trace_context.c", 
+      "src/core/ext/census/trace_context.h", 
       "src/core/ext/census/tracing.c"
     ], 
     "third_party": false, 
@@ -5551,7 +6198,9 @@
       "src/core/lib/support/backoff.h", 
       "src/core/lib/support/block_annotate.h", 
       "src/core/lib/support/env.h", 
+      "src/core/lib/support/mpscq.h", 
       "src/core/lib/support/murmur_hash.h", 
+      "src/core/lib/support/percent_encoding.h", 
       "src/core/lib/support/stack_lockfree.h", 
       "src/core/lib/support/string.h", 
       "src/core/lib/support/string_windows.h", 
@@ -5559,6 +6208,7 @@
       "src/core/lib/support/time_precise.h", 
       "src/core/lib/support/tmpfile.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "gpr_base", 
     "src": [
@@ -5614,8 +6264,12 @@
       "src/core/lib/support/log_linux.c", 
       "src/core/lib/support/log_posix.c", 
       "src/core/lib/support/log_windows.c", 
+      "src/core/lib/support/mpscq.c", 
+      "src/core/lib/support/mpscq.h", 
       "src/core/lib/support/murmur_hash.c", 
       "src/core/lib/support/murmur_hash.h", 
+      "src/core/lib/support/percent_encoding.c", 
+      "src/core/lib/support/percent_encoding.h", 
       "src/core/lib/support/slice.c", 
       "src/core/lib/support/slice_buffer.c", 
       "src/core/lib/support/stack_lockfree.c", 
@@ -5653,38 +6307,33 @@
   {
     "deps": [], 
     "headers": [
-      "include/grpc/impl/codegen/alloc.h", 
       "include/grpc/impl/codegen/atm.h", 
       "include/grpc/impl/codegen/atm_gcc_atomic.h", 
       "include/grpc/impl/codegen/atm_gcc_sync.h", 
       "include/grpc/impl/codegen/atm_windows.h", 
-      "include/grpc/impl/codegen/log.h", 
+      "include/grpc/impl/codegen/gpr_types.h", 
       "include/grpc/impl/codegen/port_platform.h", 
       "include/grpc/impl/codegen/slice.h", 
-      "include/grpc/impl/codegen/slice_buffer.h", 
       "include/grpc/impl/codegen/sync.h", 
       "include/grpc/impl/codegen/sync_generic.h", 
       "include/grpc/impl/codegen/sync_posix.h", 
-      "include/grpc/impl/codegen/sync_windows.h", 
-      "include/grpc/impl/codegen/time.h"
+      "include/grpc/impl/codegen/sync_windows.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "gpr_codegen", 
     "src": [
-      "include/grpc/impl/codegen/alloc.h", 
       "include/grpc/impl/codegen/atm.h", 
       "include/grpc/impl/codegen/atm_gcc_atomic.h", 
       "include/grpc/impl/codegen/atm_gcc_sync.h", 
       "include/grpc/impl/codegen/atm_windows.h", 
-      "include/grpc/impl/codegen/log.h", 
+      "include/grpc/impl/codegen/gpr_types.h", 
       "include/grpc/impl/codegen/port_platform.h", 
       "include/grpc/impl/codegen/slice.h", 
-      "include/grpc/impl/codegen/slice_buffer.h", 
       "include/grpc/impl/codegen/sync.h", 
       "include/grpc/impl/codegen/sync_generic.h", 
       "include/grpc/impl/codegen/sync_posix.h", 
-      "include/grpc/impl/codegen/sync_windows.h", 
-      "include/grpc/impl/codegen/time.h"
+      "include/grpc/impl/codegen/sync_windows.h"
     ], 
     "third_party": false, 
     "type": "filegroup"
@@ -5700,6 +6349,7 @@
       "include/grpc/compression.h", 
       "include/grpc/grpc.h", 
       "include/grpc/grpc_posix.h", 
+      "include/grpc/grpc_security_constants.h", 
       "include/grpc/status.h", 
       "src/core/lib/channel/channel_args.h", 
       "src/core/lib/channel/channel_stack.h", 
@@ -5707,9 +6357,11 @@
       "src/core/lib/channel/compress_filter.h", 
       "src/core/lib/channel/connected_channel.h", 
       "src/core/lib/channel/context.h", 
+      "src/core/lib/channel/deadline_filter.h", 
       "src/core/lib/channel/handshaker.h", 
       "src/core/lib/channel/http_client_filter.h", 
       "src/core/lib/channel/http_server_filter.h", 
+      "src/core/lib/channel/message_size_filter.h", 
       "src/core/lib/compression/algorithm_metadata.h", 
       "src/core/lib/compression/message_compress.h", 
       "src/core/lib/debug/trace.h", 
@@ -5717,6 +6369,7 @@
       "src/core/lib/http/httpcli.h", 
       "src/core/lib/http/parser.h", 
       "src/core/lib/iomgr/closure.h", 
+      "src/core/lib/iomgr/combiner.h", 
       "src/core/lib/iomgr/endpoint.h", 
       "src/core/lib/iomgr/endpoint_pair.h", 
       "src/core/lib/iomgr/error.h", 
@@ -5778,9 +6431,11 @@
       "src/core/lib/transport/metadata.h", 
       "src/core/lib/transport/metadata_batch.h", 
       "src/core/lib/transport/static_metadata.h", 
+      "src/core/lib/transport/timeout_encoding.h", 
       "src/core/lib/transport/transport.h", 
       "src/core/lib/transport/transport_impl.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_base", 
     "src": [
@@ -5789,6 +6444,7 @@
       "include/grpc/compression.h", 
       "include/grpc/grpc.h", 
       "include/grpc/grpc_posix.h", 
+      "include/grpc/grpc_security_constants.h", 
       "include/grpc/status.h", 
       "src/core/lib/channel/channel_args.c", 
       "src/core/lib/channel/channel_args.h", 
@@ -5801,12 +6457,16 @@
       "src/core/lib/channel/connected_channel.c", 
       "src/core/lib/channel/connected_channel.h", 
       "src/core/lib/channel/context.h", 
+      "src/core/lib/channel/deadline_filter.c", 
+      "src/core/lib/channel/deadline_filter.h", 
       "src/core/lib/channel/handshaker.c", 
       "src/core/lib/channel/handshaker.h", 
       "src/core/lib/channel/http_client_filter.c", 
       "src/core/lib/channel/http_client_filter.h", 
       "src/core/lib/channel/http_server_filter.c", 
       "src/core/lib/channel/http_server_filter.h", 
+      "src/core/lib/channel/message_size_filter.c", 
+      "src/core/lib/channel/message_size_filter.h", 
       "src/core/lib/compression/algorithm_metadata.h", 
       "src/core/lib/compression/compression.c", 
       "src/core/lib/compression/message_compress.c", 
@@ -5821,6 +6481,8 @@
       "src/core/lib/http/parser.h", 
       "src/core/lib/iomgr/closure.c", 
       "src/core/lib/iomgr/closure.h", 
+      "src/core/lib/iomgr/combiner.c", 
+      "src/core/lib/iomgr/combiner.h", 
       "src/core/lib/iomgr/endpoint.c", 
       "src/core/lib/iomgr/endpoint.h", 
       "src/core/lib/iomgr/endpoint_pair.h", 
@@ -5953,6 +6615,8 @@
       "src/core/lib/transport/metadata_batch.h", 
       "src/core/lib/transport/static_metadata.c", 
       "src/core/lib/transport/static_metadata.h", 
+      "src/core/lib/transport/timeout_encoding.c", 
+      "src/core/lib/transport/timeout_encoding.h", 
       "src/core/lib/transport/transport.c", 
       "src/core/lib/transport/transport.h", 
       "src/core/lib/transport/transport_impl.h", 
@@ -5969,8 +6633,8 @@
     "headers": [
       "src/core/ext/client_config/client_channel.h", 
       "src/core/ext/client_config/client_channel_factory.h", 
-      "src/core/ext/client_config/client_config.h", 
       "src/core/ext/client_config/connector.h", 
+      "src/core/ext/client_config/http_connect_handshaker.h", 
       "src/core/ext/client_config/initial_connect_string.h", 
       "src/core/ext/client_config/lb_policy.h", 
       "src/core/ext/client_config/lb_policy_factory.h", 
@@ -5979,11 +6643,12 @@
       "src/core/ext/client_config/resolver.h", 
       "src/core/ext/client_config/resolver_factory.h", 
       "src/core/ext/client_config/resolver_registry.h", 
+      "src/core/ext/client_config/resolver_result.h", 
       "src/core/ext/client_config/subchannel.h", 
-      "src/core/ext/client_config/subchannel_call_holder.h", 
       "src/core/ext/client_config/subchannel_index.h", 
       "src/core/ext/client_config/uri_parser.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_client_config", 
     "src": [
@@ -5992,12 +6657,12 @@
       "src/core/ext/client_config/client_channel.h", 
       "src/core/ext/client_config/client_channel_factory.c", 
       "src/core/ext/client_config/client_channel_factory.h", 
-      "src/core/ext/client_config/client_config.c", 
-      "src/core/ext/client_config/client_config.h", 
       "src/core/ext/client_config/client_config_plugin.c", 
       "src/core/ext/client_config/connector.c", 
       "src/core/ext/client_config/connector.h", 
       "src/core/ext/client_config/default_initial_connect_string.c", 
+      "src/core/ext/client_config/http_connect_handshaker.c", 
+      "src/core/ext/client_config/http_connect_handshaker.h", 
       "src/core/ext/client_config/initial_connect_string.c", 
       "src/core/ext/client_config/initial_connect_string.h", 
       "src/core/ext/client_config/lb_policy.c", 
@@ -6014,10 +6679,10 @@
       "src/core/ext/client_config/resolver_factory.h", 
       "src/core/ext/client_config/resolver_registry.c", 
       "src/core/ext/client_config/resolver_registry.h", 
+      "src/core/ext/client_config/resolver_result.c", 
+      "src/core/ext/client_config/resolver_result.h", 
       "src/core/ext/client_config/subchannel.c", 
       "src/core/ext/client_config/subchannel.h", 
-      "src/core/ext/client_config/subchannel_call_holder.c", 
-      "src/core/ext/client_config/subchannel_call_holder.h", 
       "src/core/ext/client_config/subchannel_index.c", 
       "src/core/ext/client_config/subchannel_index.h", 
       "src/core/ext/client_config/uri_parser.c", 
@@ -6031,7 +6696,6 @@
       "gpr_codegen"
     ], 
     "headers": [
-      "include/grpc/impl/codegen/byte_buffer.h", 
       "include/grpc/impl/codegen/byte_buffer_reader.h", 
       "include/grpc/impl/codegen/compression_types.h", 
       "include/grpc/impl/codegen/connectivity_state.h", 
@@ -6039,10 +6703,10 @@
       "include/grpc/impl/codegen/propagation_bits.h", 
       "include/grpc/impl/codegen/status.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_codegen", 
     "src": [
-      "include/grpc/impl/codegen/byte_buffer.h", 
       "include/grpc/impl/codegen/byte_buffer_reader.h", 
       "include/grpc/impl/codegen/compression_types.h", 
       "include/grpc/impl/codegen/connectivity_state.h", 
@@ -6061,12 +6725,16 @@
       "nanopb"
     ], 
     "headers": [
+      "src/core/ext/lb_policy/grpclb/grpclb.h", 
       "src/core/ext/lb_policy/grpclb/load_balancer_api.h", 
       "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_lb_policy_grpclb", 
     "src": [
+      "src/core/ext/lb_policy/grpclb/grpclb.c", 
+      "src/core/ext/lb_policy/grpclb/grpclb.h", 
       "src/core/ext/lb_policy/grpclb/load_balancer_api.c", 
       "src/core/ext/lb_policy/grpclb/load_balancer_api.h", 
       "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c", 
@@ -6082,6 +6750,7 @@
       "grpc_client_config"
     ], 
     "headers": [], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_lb_policy_pick_first", 
     "src": [
@@ -6097,6 +6766,7 @@
       "grpc_client_config"
     ], 
     "headers": [], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_lb_policy_round_robin", 
     "src": [
@@ -6114,6 +6784,7 @@
       "src/core/ext/load_reporting/load_reporting.h", 
       "src/core/ext/load_reporting/load_reporting_filter.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_load_reporting", 
     "src": [
@@ -6132,6 +6803,7 @@
       "grpc_client_config"
     ], 
     "headers": [], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_resolver_dns_native", 
     "src": [
@@ -6147,6 +6819,7 @@
       "grpc_client_config"
     ], 
     "headers": [], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_resolver_sockaddr", 
     "src": [
@@ -6164,7 +6837,6 @@
     ], 
     "headers": [
       "include/grpc/grpc_security.h", 
-      "include/grpc/grpc_security_constants.h", 
       "src/core/lib/security/context/security_context.h", 
       "src/core/lib/security/credentials/composite/composite_credentials.h", 
       "src/core/lib/security/credentials/credentials.h", 
@@ -6185,11 +6857,11 @@
       "src/core/lib/security/util/b64.h", 
       "src/core/lib/security/util/json_util.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_secure", 
     "src": [
       "include/grpc/grpc_security.h", 
-      "include/grpc/grpc_security_constants.h", 
       "src/core/lib/http/httpcli_security_connector.c", 
       "src/core/lib/security/context/security_context.c", 
       "src/core/lib/security/context/security_context.h", 
@@ -6245,6 +6917,8 @@
     ], 
     "headers": [
       "test/core/end2end/cq_verifier.h", 
+      "test/core/end2end/fake_resolver.h", 
+      "test/core/end2end/fixtures/http_proxy.h", 
       "test/core/end2end/fixtures/proxy.h", 
       "test/core/iomgr/endpoint_tests.h", 
       "test/core/util/grpc_profiler.h", 
@@ -6256,11 +6930,16 @@
       "test/core/util/port_server_client.h", 
       "test/core/util/slice_splitter.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_test_util_base", 
     "src": [
       "test/core/end2end/cq_verifier.c", 
       "test/core/end2end/cq_verifier.h", 
+      "test/core/end2end/fake_resolver.c", 
+      "test/core/end2end/fake_resolver.h", 
+      "test/core/end2end/fixtures/http_proxy.c", 
+      "test/core/end2end/fixtures/http_proxy.h", 
       "test/core/end2end/fixtures/proxy.c", 
       "test/core/end2end/fixtures/proxy.h", 
       "test/core/iomgr/endpoint_tests.c", 
@@ -6312,9 +6991,9 @@
       "src/core/ext/transport/chttp2/transport/internal.h", 
       "src/core/ext/transport/chttp2/transport/status_conversion.h", 
       "src/core/ext/transport/chttp2/transport/stream_map.h", 
-      "src/core/ext/transport/chttp2/transport/timeout_encoding.h", 
       "src/core/ext/transport/chttp2/transport/varint.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_transport_chttp2", 
     "src": [
@@ -6356,8 +7035,6 @@
       "src/core/ext/transport/chttp2/transport/stream_lists.c", 
       "src/core/ext/transport/chttp2/transport/stream_map.c", 
       "src/core/ext/transport/chttp2/transport/stream_map.h", 
-      "src/core/ext/transport/chttp2/transport/timeout_encoding.c", 
-      "src/core/ext/transport/chttp2/transport/timeout_encoding.h", 
       "src/core/ext/transport/chttp2/transport/varint.c", 
       "src/core/ext/transport/chttp2/transport/varint.h", 
       "src/core/ext/transport/chttp2/transport/writing.c"
@@ -6372,6 +7049,7 @@
     "headers": [
       "src/core/ext/transport/chttp2/alpn/alpn.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_transport_chttp2_alpn", 
     "src": [
@@ -6389,6 +7067,7 @@
       "grpc_transport_chttp2"
     ], 
     "headers": [], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_transport_chttp2_client_insecure", 
     "src": [
@@ -6407,6 +7086,7 @@
       "grpc_transport_chttp2"
     ], 
     "headers": [], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_transport_chttp2_client_secure", 
     "src": [
@@ -6422,6 +7102,7 @@
       "grpc_transport_chttp2"
     ], 
     "headers": [], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_transport_chttp2_server_insecure", 
     "src": [
@@ -6439,6 +7120,7 @@
       "grpc_transport_chttp2"
     ], 
     "headers": [], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_transport_chttp2_server_secure", 
     "src": [
@@ -6458,6 +7140,7 @@
       "include/grpc/grpc_security_constants.h", 
       "third_party/objective_c/Cronet/cronet_c_for_grpc.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "grpc_transport_cronet_client_secure", 
     "src": [
@@ -6479,6 +7162,7 @@
       "third_party/nanopb/pb_decode.h", 
       "third_party/nanopb/pb_encode.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "nanopb", 
     "src": [], 
@@ -6496,6 +7180,7 @@
       "src/core/lib/tsi/transport_security.h", 
       "src/core/lib/tsi/transport_security_interface.h"
     ], 
+    "is_filegroup": true, 
     "language": "c", 
     "name": "tsi", 
     "src": [
@@ -6513,7 +7198,6 @@
   }, 
   {
     "deps": [
-      "grpc", 
       "grpc++_codegen_base"
     ], 
     "headers": [
@@ -6565,9 +7249,11 @@
       "include/grpc++/support/sync_stream.h", 
       "include/grpc++/support/time.h", 
       "src/cpp/client/create_channel_internal.h", 
+      "src/cpp/common/channel_filter.h", 
       "src/cpp/server/dynamic_thread_pool.h", 
       "src/cpp/server/thread_pool_interface.h"
     ], 
+    "is_filegroup": true, 
     "language": "c++", 
     "name": "grpc++_base", 
     "src": [
@@ -6618,35 +7304,35 @@
       "include/grpc++/support/stub_options.h", 
       "include/grpc++/support/sync_stream.h", 
       "include/grpc++/support/time.h", 
-      "src/cpp/client/channel.cc", 
+      "src/cpp/client/channel_cc.cc", 
       "src/cpp/client/client_context.cc", 
       "src/cpp/client/create_channel.cc", 
       "src/cpp/client/create_channel_internal.cc", 
       "src/cpp/client/create_channel_internal.h", 
       "src/cpp/client/create_channel_posix.cc", 
-      "src/cpp/client/credentials.cc", 
+      "src/cpp/client/credentials_cc.cc", 
       "src/cpp/client/generic_stub.cc", 
-      "src/cpp/client/insecure_credentials.cc", 
       "src/cpp/common/channel_arguments.cc", 
-      "src/cpp/common/completion_queue.cc", 
+      "src/cpp/common/channel_filter.cc", 
+      "src/cpp/common/channel_filter.h", 
+      "src/cpp/common/completion_queue_cc.cc", 
       "src/cpp/common/core_codegen.cc", 
       "src/cpp/common/rpc_method.cc", 
       "src/cpp/server/async_generic_service.cc", 
       "src/cpp/server/create_default_thread_pool.cc", 
       "src/cpp/server/dynamic_thread_pool.cc", 
       "src/cpp/server/dynamic_thread_pool.h", 
-      "src/cpp/server/insecure_server_credentials.cc", 
-      "src/cpp/server/server.cc", 
       "src/cpp/server/server_builder.cc", 
+      "src/cpp/server/server_cc.cc", 
       "src/cpp/server/server_context.cc", 
       "src/cpp/server/server_credentials.cc", 
       "src/cpp/server/server_posix.cc", 
       "src/cpp/server/thread_pool_interface.h", 
-      "src/cpp/util/byte_buffer.cc", 
-      "src/cpp/util/slice.cc", 
+      "src/cpp/util/byte_buffer_cc.cc", 
+      "src/cpp/util/slice_cc.cc", 
       "src/cpp/util/status.cc", 
       "src/cpp/util/string_ref.cc", 
-      "src/cpp/util/time.cc"
+      "src/cpp/util/time_cc.cc"
     ], 
     "third_party": false, 
     "type": "filegroup"
@@ -6679,6 +7365,7 @@
       "include/grpc++/impl/codegen/service_type.h", 
       "include/grpc++/impl/codegen/status.h", 
       "include/grpc++/impl/codegen/status_code_enum.h", 
+      "include/grpc++/impl/codegen/status_helper.h", 
       "include/grpc++/impl/codegen/string_ref.h", 
       "include/grpc++/impl/codegen/stub_options.h", 
       "include/grpc++/impl/codegen/sync.h", 
@@ -6687,6 +7374,7 @@
       "include/grpc++/impl/codegen/sync_stream.h", 
       "include/grpc++/impl/codegen/time.h"
     ], 
+    "is_filegroup": true, 
     "language": "c++", 
     "name": "grpc++_codegen_base", 
     "src": [
@@ -6713,6 +7401,7 @@
       "include/grpc++/impl/codegen/service_type.h", 
       "include/grpc++/impl/codegen/status.h", 
       "include/grpc++/impl/codegen/status_code_enum.h", 
+      "include/grpc++/impl/codegen/status_helper.h", 
       "include/grpc++/impl/codegen/string_ref.h", 
       "include/grpc++/impl/codegen/stub_options.h", 
       "include/grpc++/impl/codegen/sync.h", 
@@ -6729,6 +7418,7 @@
       "grpc++_codegen_base"
     ], 
     "headers": [], 
+    "is_filegroup": true, 
     "language": "c++", 
     "name": "grpc++_codegen_base_src", 
     "src": [
@@ -6745,6 +7435,7 @@
     "headers": [
       "include/grpc++/impl/codegen/proto_utils.h"
     ], 
+    "is_filegroup": true, 
     "language": "c++", 
     "name": "grpc++_codegen_proto", 
     "src": [
@@ -6758,6 +7449,7 @@
     "headers": [
       "include/grpc++/impl/codegen/config_protobuf.h"
     ], 
+    "is_filegroup": true, 
     "language": "c++", 
     "name": "grpc++_config_proto", 
     "src": [
@@ -6774,6 +7466,7 @@
       "include/grpc++/ext/reflection.grpc.pb.h", 
       "include/grpc++/ext/reflection.pb.h"
     ], 
+    "is_filegroup": true, 
     "language": "c++", 
     "name": "grpc++_reflection_proto", 
     "src": [
@@ -6784,5 +7477,23 @@
     ], 
     "third_party": false, 
     "type": "filegroup"
+  }, 
+  {
+    "deps": [
+      "grpc++_codegen_base"
+    ], 
+    "headers": [
+      "include/grpc++/impl/codegen/thrift_serializer.h", 
+      "include/grpc++/impl/codegen/thrift_utils.h"
+    ], 
+    "is_filegroup": true, 
+    "language": "c++", 
+    "name": "thrift_util", 
+    "src": [
+      "include/grpc++/impl/codegen/thrift_serializer.h", 
+      "include/grpc++/impl/codegen/thrift_utils.h"
+    ], 
+    "third_party": false, 
+    "type": "filegroup"
   }
 ]
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index d94301b..c339506 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -182,6 +182,48 @@
     "flaky": false, 
     "gtest": false, 
     "language": "c", 
+    "name": "census_resource_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
+    "name": "census_trace_context_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
     "name": "channel_create_test", 
     "platforms": [
       "linux", 
@@ -282,6 +324,27 @@
       "posix", 
       "windows"
     ], 
+    "cpu_cost": 30, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
+    "name": "combiner_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
     "flaky": false, 
@@ -329,6 +392,27 @@
     "flaky": false, 
     "gtest": false, 
     "language": "c", 
+    "name": "connection_refused_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
     "name": "dns_resolver_connectivity_test", 
     "platforms": [
       "linux", 
@@ -684,6 +768,48 @@
       "posix", 
       "windows"
     ], 
+    "cpu_cost": 30, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
+    "name": "gpr_mpscq_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
+    "name": "gpr_percent_encoding_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
     "flaky": false, 
@@ -2240,6 +2366,27 @@
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
+    "name": "filter_end2end_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
     "name": "generic_end2end_test", 
     "platforms": [
       "linux", 
@@ -2282,6 +2429,27 @@
     "flaky": false, 
     "gtest": true, 
     "language": "c++", 
+    "name": "grpc_tool_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "gtest": true, 
+    "language": "c++", 
     "name": "grpclb_api_test", 
     "platforms": [
       "linux", 
@@ -2301,6 +2469,27 @@
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
     "flaky": false, 
+    "gtest": false, 
+    "language": "c++", 
+    "name": "grpclb_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
     "gtest": true, 
     "language": "c++", 
     "name": "hybrid_end2end_test", 
@@ -2796,25 +2985,6 @@
     "flaky": false, 
     "gtest": false, 
     "language": "c", 
-    "name": "bad_ssl_alpn_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ]
-  }, 
-  {
-    "args": [], 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "gtest": false, 
-    "language": "c", 
     "name": "bad_ssl_cert_test", 
     "platforms": [
       "linux", 
@@ -4678,6 +4848,28 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -4832,6 +5024,28 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -4920,6 +5134,28 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -5140,6 +5376,28 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -5558,6 +5816,28 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -5712,6 +5992,28 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -5800,6 +6102,28 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -6020,6 +6344,28 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -6424,6 +6770,27 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -6571,6 +6938,27 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -6655,6 +7043,27 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -6865,6 +7274,27 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fakesec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -7190,6 +7620,26 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -7330,6 +7780,26 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -7410,6 +7880,26 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -7590,6 +8080,26 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_metadata"
     ], 
     "ci_platforms": [
@@ -7978,6 +8488,28 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -8132,6 +8664,28 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -8220,6 +8774,28 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -8440,6 +9016,28 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -8774,6 +9372,22 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -8886,6 +9500,22 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -8950,6 +9580,22 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -9110,6 +9756,22 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -9498,6 +10160,28 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -9630,6 +10314,28 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -9938,6 +10644,28 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -10053,6 +10781,930 @@
     "ci_platforms": [
       "windows", 
       "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "binary_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "call_creds"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_accept"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_client_done"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_invoke"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_before_invoke"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_in_a_vacuum"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_with_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "compressed_payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "connectivity"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "default_host"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "disappearing_server"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "empty_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_causes_close"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "graceful_server_shutdown"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "high_initial_seqno"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "hpack_size"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "idempotent_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "invoke_large_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "large_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "max_concurrent_streams"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "max_message_length"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "negative_deadline"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "network_status_change"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "no_op"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "ping"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "ping_pong_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "registered_call"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "request_with_flags"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "request_with_payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "server_finishes_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "shutdown_finishes_calls"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "shutdown_finishes_tags"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_delayed_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "streaming_error_response"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "trailing_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "bad_hostname"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
       "mac", 
       "posix"
     ], 
@@ -10060,7 +11712,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10082,7 +11734,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10104,7 +11756,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10126,7 +11778,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10148,7 +11800,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10170,7 +11822,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10192,7 +11844,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10214,7 +11866,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10236,7 +11888,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10258,7 +11910,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10280,7 +11932,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10302,7 +11954,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10324,7 +11976,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10346,7 +11998,29 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10368,7 +12042,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10390,7 +12064,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10412,7 +12086,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10434,7 +12108,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10456,7 +12130,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10478,7 +12152,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10500,7 +12174,29 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10522,7 +12218,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10544,7 +12240,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10566,7 +12262,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10588,7 +12284,29 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10610,7 +12328,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10632,7 +12350,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10654,7 +12372,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10676,7 +12394,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10698,7 +12416,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10720,7 +12438,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10742,7 +12460,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10764,7 +12482,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10786,7 +12504,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10808,7 +12526,29 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10830,7 +12570,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10852,7 +12592,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10874,7 +12614,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10896,7 +12636,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -10918,7 +12658,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_test", 
+    "name": "h2_load_reporting_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -11222,6 +12962,27 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -11369,6 +13130,27 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -11453,6 +13235,27 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -11663,6 +13466,27 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_oauth2_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -12020,6 +13844,27 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -12146,6 +13991,27 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_message_length"
     ], 
     "ci_platforms": [
@@ -12209,6 +14075,27 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -12377,6 +14264,27 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -12713,6 +14621,27 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -12860,6 +14789,27 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -12944,6 +14894,27 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -13133,6 +15104,27 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_metadata"
     ], 
     "ci_platforms": [
@@ -13448,6 +15440,27 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -13574,6 +15587,27 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -13847,6 +15881,27 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair+trace_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_metadata"
     ], 
     "ci_platforms": [
@@ -14162,6 +16217,27 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_1byte_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -14309,6 +16385,27 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_1byte_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -14393,6 +16490,27 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_1byte_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -14582,6 +16700,27 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_1byte_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_metadata"
     ], 
     "ci_platforms": [
@@ -14974,6 +17113,28 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -15128,6 +17289,28 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -15216,6 +17399,28 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -15436,6 +17641,28 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -15854,6 +18081,28 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -16008,6 +18257,28 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -16096,6 +18367,28 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -16316,6 +18609,28 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_cert_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -16678,6 +18993,27 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -16804,6 +19140,27 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_message_length"
     ], 
     "ci_platforms": [
@@ -16867,6 +19224,27 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -17035,6 +19413,27 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_ssl_proxy_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -17400,6 +19799,26 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -17540,6 +19959,26 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -17620,6 +20059,26 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -17820,6 +20279,26 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -18206,6 +20685,28 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -18360,6 +20861,28 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -18448,6 +20971,28 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -18668,6 +21213,28 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_census_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -19064,6 +21631,28 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -19218,6 +21807,28 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -19306,6 +21917,28 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -19526,6 +22159,28 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_compress_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -19836,6 +22491,26 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -19976,6 +22651,26 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -20056,6 +22751,26 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -20236,6 +22951,26 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_fd_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_metadata"
     ], 
     "ci_platforms": [
@@ -20602,6 +23337,28 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -20756,6 +23513,28 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -20844,6 +23623,28 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -21064,6 +23865,28 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -21382,6 +24205,22 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -21494,6 +24333,22 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -21558,6 +24413,22 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -21718,6 +24589,22 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+pipe_nosec_test", 
+    "platforms": [
+      "linux"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -22084,6 +24971,28 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -22216,6 +25125,28 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -22524,6 +25455,28 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_full+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -22639,6 +25592,909 @@
     "ci_platforms": [
       "windows", 
       "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "binary_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_accept"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_client_done"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_after_invoke"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_before_invoke"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_in_a_vacuum"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "cancel_with_status"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "compressed_payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "connectivity"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "default_host"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "disappearing_server"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "empty_batch"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_causes_close"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "graceful_server_shutdown"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "high_initial_seqno"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "hpack_size"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "idempotent_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "invoke_large_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "large_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "max_concurrent_streams"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "max_message_length"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "negative_deadline"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "network_status_change"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "no_op"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "ping"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "ping_pong_streaming"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "registered_call"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "request_with_flags"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "request_with_payload"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "server_finishes_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "shutdown_finishes_calls"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "shutdown_finishes_tags"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_delayed_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "streaming_error_response"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "trailing_metadata"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_http_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "bad_hostname"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
       "mac", 
       "posix"
     ], 
@@ -22646,7 +26502,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22668,7 +26524,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22690,7 +26546,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22712,7 +26568,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22734,7 +26590,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22756,7 +26612,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22778,7 +26634,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22800,7 +26656,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22822,7 +26678,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22844,7 +26700,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22866,7 +26722,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22888,7 +26744,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22910,7 +26766,29 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22932,7 +26810,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22954,7 +26832,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22976,7 +26854,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -22998,7 +26876,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23020,7 +26898,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23042,7 +26920,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23064,7 +26942,29 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23086,7 +26986,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23108,7 +27008,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23130,7 +27030,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23152,7 +27052,29 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23174,7 +27096,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23196,7 +27118,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23218,7 +27140,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23240,7 +27162,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23262,7 +27184,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23284,7 +27206,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23306,7 +27228,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23328,7 +27250,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23350,7 +27272,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23372,7 +27294,29 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23394,7 +27338,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23416,7 +27360,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23438,7 +27382,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23460,7 +27404,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23482,7 +27426,7 @@
     "exclude_configs": [], 
     "flaky": false, 
     "language": "c", 
-    "name": "h2_loadreporting_nosec_test", 
+    "name": "h2_load_reporting_nosec_test", 
     "platforms": [
       "windows", 
       "linux", 
@@ -23723,6 +27667,27 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -23849,6 +27814,27 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_message_length"
     ], 
     "ci_platforms": [
@@ -23912,6 +27898,27 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -24080,6 +28087,27 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_proxy_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -24395,6 +28423,27 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -24542,6 +28591,27 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -24626,6 +28696,27 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -24815,6 +28906,27 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_metadata"
     ], 
     "ci_platforms": [
@@ -25109,6 +29221,27 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -25235,6 +29368,27 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -25508,6 +29662,27 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair+trace_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_metadata"
     ], 
     "ci_platforms": [
@@ -25822,6 +29997,29 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [
+      "msan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_1byte_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -25983,6 +30181,29 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [
+      "msan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_1byte_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -26075,6 +30296,29 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [
+      "msan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_1byte_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -26282,6 +30526,29 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "windows", 
+      "linux", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [
+      "msan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_sockpair_1byte_nosec_test", 
+    "platforms": [
+      "windows", 
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_metadata"
     ], 
     "ci_platforms": [
@@ -26614,6 +30881,26 @@
   }, 
   {
     "args": [
+      "filter_call_init_fails"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "filter_causes_close"
     ], 
     "ci_platforms": [
@@ -26754,6 +31041,26 @@
   }, 
   {
     "args": [
+      "load_reporting_hook"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "max_concurrent_streams"
     ], 
     "ci_platforms": [
@@ -26834,6 +31141,26 @@
   }, 
   {
     "args": [
+      "no_logging"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "no_op"
     ], 
     "ci_platforms": [
@@ -27034,6 +31361,26 @@
   }, 
   {
     "args": [
+      "simple_cacheable_request"
+    ], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "h2_uds_nosec_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [
       "simple_delayed_request"
     ], 
     "ci_platforms": [
@@ -27139,10 +31486,7 @@
     ], 
     "boringssl": true, 
     "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+      "linux"
     ], 
     "cpu_cost": 2, 
     "defaults": "boringssl", 
@@ -27151,10 +31495,7 @@
     "language": "c++", 
     "name": "json_run_localhost", 
     "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+      "linux"
     ], 
     "shortname": "json_run_localhost:cpp_generic_async_streaming_ping_pong_secure", 
     "timeout_seconds": 180
@@ -27162,149 +31503,11 @@
   {
     "args": [
       "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
-    ], 
-    "boringssl": true, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 2, 
-    "defaults": "boringssl", 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c++", 
-    "name": "json_run_localhost", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_ping_pong_secure", 
-    "timeout_seconds": 180
-  }, 
-  {
-    "args": [
-      "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_unary_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
-    ], 
-    "boringssl": true, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 2, 
-    "defaults": "boringssl", 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c++", 
-    "name": "json_run_localhost", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_unary_ping_pong_secure", 
-    "timeout_seconds": 180
-  }, 
-  {
-    "args": [
-      "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_unary_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
-    ], 
-    "boringssl": true, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 2, 
-    "defaults": "boringssl", 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c++", 
-    "name": "json_run_localhost", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_ping_pong_secure", 
-    "timeout_seconds": 180
-  }, 
-  {
-    "args": [
-      "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_unary_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
-    ], 
-    "boringssl": true, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 8, 
-    "defaults": "boringssl", 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c++", 
-    "name": "json_run_localhost", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_unary_qps_unconstrained_secure", 
-    "timeout_seconds": 180
-  }, 
-  {
-    "args": [
-      "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
-    ], 
-    "boringssl": true, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 8, 
-    "defaults": "boringssl", 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c++", 
-    "name": "json_run_localhost", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_qps_unconstrained_secure", 
-    "timeout_seconds": 180
-  }, 
-  {
-    "args": [
-      "--scenarios_json", 
       "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
     ], 
     "boringssl": true, 
     "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+      "linux"
     ], 
     "cpu_cost": 8, 
     "defaults": "boringssl", 
@@ -27313,10 +31516,7 @@
     "language": "c++", 
     "name": "json_run_localhost", 
     "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+      "linux"
     ], 
     "shortname": "json_run_localhost:cpp_generic_async_streaming_qps_unconstrained_secure", 
     "timeout_seconds": 180
@@ -27328,10 +31528,7 @@
     ], 
     "boringssl": true, 
     "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+      "linux"
     ], 
     "cpu_cost": 1, 
     "defaults": "boringssl", 
@@ -27340,10 +31537,7 @@
     "language": "c++", 
     "name": "json_run_localhost", 
     "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+      "linux"
     ], 
     "shortname": "json_run_localhost:cpp_generic_async_streaming_qps_one_server_core_secure", 
     "timeout_seconds": 180
@@ -27351,14 +31545,179 @@
   {
     "args": [
       "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_unary_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_ping_pong_secure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_unary_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 16, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 8, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_qps_unconstrained_secure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_unary_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_unary_ping_pong_secure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_unary_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 8, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_unary_qps_unconstrained_secure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_ping_pong_secure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 16, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 8, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_qps_unconstrained_secure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_ping_pong_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_ping_pong_secure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 8, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_qps_unconstrained_secure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
       "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
     ], 
     "boringssl": true, 
     "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+      "linux"
     ], 
     "cpu_cost": 2, 
     "defaults": "boringssl", 
@@ -27367,10 +31726,7 @@
     "language": "c++", 
     "name": "json_run_localhost", 
     "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+      "linux"
     ], 
     "shortname": "json_run_localhost:cpp_generic_async_streaming_ping_pong_insecure", 
     "timeout_seconds": 180
@@ -27378,149 +31734,11 @@
   {
     "args": [
       "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
-    ], 
-    "boringssl": true, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 2, 
-    "defaults": "boringssl", 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c++", 
-    "name": "json_run_localhost", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_ping_pong_insecure", 
-    "timeout_seconds": 180
-  }, 
-  {
-    "args": [
-      "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_unary_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
-    ], 
-    "boringssl": true, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 2, 
-    "defaults": "boringssl", 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c++", 
-    "name": "json_run_localhost", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_unary_ping_pong_insecure", 
-    "timeout_seconds": 180
-  }, 
-  {
-    "args": [
-      "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_unary_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
-    ], 
-    "boringssl": true, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 2, 
-    "defaults": "boringssl", 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c++", 
-    "name": "json_run_localhost", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_ping_pong_insecure", 
-    "timeout_seconds": 180
-  }, 
-  {
-    "args": [
-      "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_unary_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
-    ], 
-    "boringssl": true, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 8, 
-    "defaults": "boringssl", 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c++", 
-    "name": "json_run_localhost", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_unary_qps_unconstrained_insecure", 
-    "timeout_seconds": 180
-  }, 
-  {
-    "args": [
-      "--scenarios_json", 
-      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
-    ], 
-    "boringssl": true, 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 8, 
-    "defaults": "boringssl", 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c++", 
-    "name": "json_run_localhost", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_qps_unconstrained_insecure", 
-    "timeout_seconds": 180
-  }, 
-  {
-    "args": [
-      "--scenarios_json", 
       "{\"scenarios\": [{\"name\": \"cpp_generic_async_streaming_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"server_type\": \"ASYNC_GENERIC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"bytebuf_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
     ], 
     "boringssl": true, 
     "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+      "linux"
     ], 
     "cpu_cost": 8, 
     "defaults": "boringssl", 
@@ -27529,10 +31747,7 @@
     "language": "c++", 
     "name": "json_run_localhost", 
     "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+      "linux"
     ], 
     "shortname": "json_run_localhost:cpp_generic_async_streaming_qps_unconstrained_insecure", 
     "timeout_seconds": 180
@@ -27544,10 +31759,7 @@
     ], 
     "boringssl": true, 
     "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+      "linux"
     ], 
     "cpu_cost": 1, 
     "defaults": "boringssl", 
@@ -27556,16 +31768,181 @@
     "language": "c++", 
     "name": "json_run_localhost", 
     "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
+      "linux"
     ], 
     "shortname": "json_run_localhost:cpp_generic_async_streaming_qps_one_server_core_insecure", 
     "timeout_seconds": 180
   }, 
   {
     "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_unary_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_ping_pong_insecure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_unary_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 16, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 8, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_unary_qps_unconstrained_insecure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_unary_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_unary_ping_pong_insecure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_unary_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"UNARY\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 8, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_unary_qps_unconstrained_insecure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_ping_pong_insecure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_sync_streaming_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"client_config\": {\"client_type\": \"SYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 16, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 8, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_sync_streaming_qps_unconstrained_insecure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_ping_pong_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 1, \"core_limit\": 1, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 1, \"async_client_threads\": 1, \"outstanding_rpcs_per_channel\": 1, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 1}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 2, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_ping_pong_insecure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
+      "--scenarios_json", 
+      "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_streaming_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": null, \"server_type\": \"ASYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}"
+    ], 
+    "boringssl": true, 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 8, 
+    "defaults": "boringssl", 
+    "exclude_configs": [], 
+    "flaky": false, 
+    "language": "c++", 
+    "name": "json_run_localhost", 
+    "platforms": [
+      "linux"
+    ], 
+    "shortname": "json_run_localhost:cpp_protobuf_async_streaming_qps_unconstrained_insecure", 
+    "timeout_seconds": 180
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/00.bin"
     ], 
     "ci_platforms": [
@@ -31214,6 +35591,25 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/22967e8ed837f03b76a980cc1d25054fb84b40e9"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/22c9ed2979d9963bce6500997f1e0433988e7e37"
     ], 
     "ci_platforms": [
@@ -34178,6 +38574,25 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/3f464011f8620f227309f6b2c84df6fffb8ed962"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/3f47ad9ab401599f42d3c4f37ab9f702e3ff0fc9"
     ], 
     "ci_platforms": [
@@ -48067,6 +52482,25 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/crash-15070b2a2719ed8a6cbbaac25da02b7085993648"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/crash-1b9aeaf762bb1a972dda8f3a455df2628efd693b"
     ], 
     "ci_platforms": [
@@ -81982,6 +86416,804 @@
   }, 
   {
     "args": [
+      "test/core/support/percent_decode_corpus/04cb8ccc553f9b2f5e52c421aff6d1c954d3dae6"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/0dd8f3a63745b3a2d39791559b5c1b311447b537"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/17eeaca784409adbe43365c32ac87915d736bba3"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/2040c1ff65f52a7ae668c2c8f324de5dacc9d695"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/26b0d1da23027ae54db96e125e4a9e98842d77fb"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/2a089c0db45acdb4c6ed8e7ff81ca7235792c0b9"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/35b7b3bc3a740d5c3abca0d75b53f0e1e1ee998a"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/36367ba1adba47a1cbc3a88707fde8cc7abdc248"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/39c2ba51548a0beaf0d6d1164531f1447dc311b5"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/56d08fea787c041395c6697ce26cfbc0decbe688"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/678d981fdabb9f0d6640235cf1719dd1e1e66ae9"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/68751961609ec010565de0aa87521dcbf0722c5d"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/7875c06c6f03c9aa2f8e9c59f8d8957c8a32e759"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/7b302090e090a5829b6d1dd7be30bd4e36a7e60f"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/875e1022169c9e4c541a9ad894e69e989df22ba1"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/8c1051ce066f5a26de9a9d133180621d0da957b4"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/8e084e628ab83a18ac7ca7cb3506525263655c63"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/9d316c4675f40ddccaf8f1cc7aea94170b1e4223"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/ad1c7c11d18a7d116e2c2ef4d4c5afb1270836ae"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/b471f94aa4facf502e622e4a248f1ba4063ae681"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/bf52ece030f16136d46e0dc97f58d60a0d8a1f0b"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/d5b2a7177339ba2b7ce2f60e5f4459bef1e72758"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/de867b64c54a7ed773dc611fc5cd2f17c5433113"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/e3948dbe004950591630dd5c52f4e0fcbd5e388a"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/e7064f0b80f61dbc65915311032d27baa569ae2a"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_decode_corpus/xyz"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_decode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/0d3ee7fa54e6c66103965fd4409b044ba7db6c3f"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/2e7ccf75e27b9501e3b28cf1c50ed0c45ab7c226"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/55bb859f3942c462b03b7cbcf22ab4a0ac9705cf"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/56070cecd54c845b6d4334953b17b712eb000d93"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/61f50e891bf7ff5eb7a7af206f1e25d77f8756e7"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/6e0c60cefc704c7940e475a87dd9ae423061cb5a"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/7271ebcc6d22a0f186f7bc3c1973a7ed1bec8d8e"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/74c83ece3e2920a67593a9be9c82468f16cbb969"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/98e004fd2a9f141a7a019720820080e12d637c06"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/ba2c1e98227aa21ea3bb2ca4d0e504119717da8b"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/c16b9fd45370d4afb5d3ebd307a6e263c25ffd45"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/d58c3cd4eab9b6d2343abfa1c25c90a383fe0ec3"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/e2619218ede30d2b7b8ecd601a9f0ae754b728b4"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/f93b3653e453f0e3eea3198001be6ce46e64bd21"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/fd41d029c7682ad3d1c40a9fd017a4c85b673a54"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/support/percent_encode_corpus/xyz"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "percent_encode_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/01c008fa.bin"
     ], 
     "ci_platforms": [
diff --git a/tools/tsan_suppressions.txt b/tools/tsan_suppressions.txt
index 09e68cd..765fa09 100644
--- a/tools/tsan_suppressions.txt
+++ b/tools/tsan_suppressions.txt
@@ -6,3 +6,6 @@
 race:ssleay_rand_add
 race:ssleay_rand_bytes
 race:__sleep_for
+# protobuf has an idempotent write race in ByteSize
+# https://github.com/google/protobuf/issues/2169
+race:ByteSize
diff --git a/vsprojects/README.md b/vsprojects/README.md
index b95b468..56d9f56 100644
--- a/vsprojects/README.md
+++ b/vsprojects/README.md
@@ -89,3 +89,18 @@
 2. Open solution `third_party\protobuf\cmake\protobuf.sln` and build it in Release mode. That will build libraries `libprotobuf.lib` and `libprotoc.lib` needed for the next step.
 
 3. Open solution `vsprojects\grpc_protoc_plugins.sln` and build it in Release mode. As a result, you should obtain a set of gRPC protoc plugin binaries (`grpc_cpp_plugin.exe`, `grpc_csharp_plugin.exe`, ...)
+
+#Building using CMake (with BoringSSL)
+1. Install [Active State Perl](http://www.activestate.com/activeperl/) (`choco install activeperl`)
+2. Install [Ninja](https://ninja-build.org/) (`choco install ninja`)
+2. Install [Go](https://golang.org/dl/) (`choco install golang`)
+3. Install [yasm](http://yasm.tortall.net/) and add it to `PATH` (`choco install yasm`)
+4. Update boringssl sumbodule to `master`
+5. Run this commads in grpc directory:
+```
+> md .build
+> cd .build
+> call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" x64
+> cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release
+> cmake --build .
+```
diff --git a/vsprojects/buildtests_c.sln b/vsprojects/buildtests_c.sln
index 7232440..f8824cc 100644
--- a/vsprojects/buildtests_c.sln
+++ b/vsprojects/buildtests_c.sln
@@ -109,6 +109,28 @@
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "census_resource_test", "vcxproj\test\census_resource_test\census_resource_test.vcxproj", "{18CF99B5-3C61-EC3D-9509-3C95334C3B88}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+	ProjectSection(ProjectDependencies) = postProject
+		{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B} = {17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}
+		{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
+		{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
+		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "census_trace_context_test", "vcxproj\test\census_trace_context_test\census_trace_context_test.vcxproj", "{14511310-FAE4-C287-31DC-D6F456D10AF3}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+	ProjectSection(ProjectDependencies) = postProject
+		{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B} = {17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}
+		{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
+		{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
+		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
+	EndProjectSection
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "channel_create_test", "vcxproj\test\channel_create_test\channel_create_test.vcxproj", "{AFC88484-3A2E-32BC-25B2-23DF741D4F3D}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
@@ -164,6 +186,17 @@
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "combiner_test", "vcxproj\test\combiner_test\combiner_test.vcxproj", "{C237D1E4-8825-80BA-1FC3-5E147E53E96E}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+	ProjectSection(ProjectDependencies) = postProject
+		{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B} = {17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}
+		{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
+		{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
+		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
+	EndProjectSection
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "compression_test", "vcxproj\test\compression_test\compression_test.vcxproj", "{5AFE7D17-A4A7-D68E-4491-CBC852F9D2A0}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
@@ -198,6 +231,17 @@
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "connection_refused_test", "vcxproj\test\connection_refused_test\connection_refused_test.vcxproj", "{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+	ProjectSection(ProjectDependencies) = postProject
+		{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B} = {17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}
+		{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
+		{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
+		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
+	EndProjectSection
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dns_resolver_connectivity_test", "vcxproj\test\dns_resolver_connectivity_test\dns_resolver_connectivity_test.vcxproj", "{F7B6FE68-E847-D7CA-4062-E737E542BCC3}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
@@ -289,6 +333,11 @@
         	lib = "False"
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gen_percent_encoding_tables", "vcxproj\.\gen_percent_encoding_tables\gen_percent_encoding_tables.vcxproj", "{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gpr", "vcxproj\.\gpr\gpr.vcxproj", "{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}"
 	ProjectSection(myProperties) = preProject
         	lib = "True"
@@ -366,6 +415,24 @@
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gpr_mpscq_test", "vcxproj\test\gpr_mpscq_test\gpr_mpscq_test.vcxproj", "{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+	ProjectSection(ProjectDependencies) = postProject
+		{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
+		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gpr_percent_encoding_test", "vcxproj\test\gpr_percent_encoding_test\gpr_percent_encoding_test.vcxproj", "{8313AE17-FCFA-8110-95C7-7AF2F814D188}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+	ProjectSection(ProjectDependencies) = postProject
+		{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
+		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
+	EndProjectSection
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gpr_slice_buffer_test", "vcxproj\test\gpr_slice_buffer_test\gpr_slice_buffer_test.vcxproj", "{E679773D-DE89-AEBB-9787-59019989B825}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
@@ -748,7 +815,7 @@
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_loadreporting_nosec_test", "vcxproj\test/end2end/fixtures\h2_loadreporting_nosec_test\h2_loadreporting_nosec_test.vcxproj", "{679EA55C-7399-53E8-79F0-82FBDB3DDE07}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_http_proxy_nosec_test", "vcxproj\test/end2end/fixtures\h2_http_proxy_nosec_test\h2_http_proxy_nosec_test.vcxproj", "{58EA8DAE-6E50-45A3-0CCC-5165D824380E}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
 	EndProjectSection
@@ -760,7 +827,31 @@
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_loadreporting_test", "vcxproj\test/end2end/fixtures\h2_loadreporting_test\h2_loadreporting_test.vcxproj", "{B107130E-EA33-C114-9CB6-78A18C929F64}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_http_proxy_test", "vcxproj\test/end2end/fixtures\h2_http_proxy_test\h2_http_proxy_test.vcxproj", "{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+	ProjectSection(ProjectDependencies) = postProject
+		{1F1F9084-2A93-B80E-364F-5754894AFAB4} = {1F1F9084-2A93-B80E-364F-5754894AFAB4}
+		{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B} = {17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}
+		{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
+		{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
+		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_load_reporting_nosec_test", "vcxproj\test/end2end/fixtures\h2_load_reporting_nosec_test\h2_load_reporting_nosec_test.vcxproj", "{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+	ProjectSection(ProjectDependencies) = postProject
+		{47C2CB41-4E9F-58B6-F606-F6FAED5D00ED} = {47C2CB41-4E9F-58B6-F606-F6FAED5D00ED}
+		{0A7E7F92-FDEA-40F1-A9EC-3BA484F98BBF} = {0A7E7F92-FDEA-40F1-A9EC-3BA484F98BBF}
+		{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5} = {46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}
+		{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
+		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h2_load_reporting_test", "vcxproj\test/end2end/fixtures\h2_load_reporting_test\h2_load_reporting_test.vcxproj", "{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
 	EndProjectSection
@@ -1599,6 +1690,38 @@
 		{5C1CFC2D-AF3C-D7CB-BA74-D267E91CBC73}.Release-DLL|Win32.Build.0 = Release|Win32
 		{5C1CFC2D-AF3C-D7CB-BA74-D267E91CBC73}.Release-DLL|x64.ActiveCfg = Release|x64
 		{5C1CFC2D-AF3C-D7CB-BA74-D267E91CBC73}.Release-DLL|x64.Build.0 = Release|x64
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Debug|Win32.ActiveCfg = Debug|Win32
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Debug|x64.ActiveCfg = Debug|x64
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Release|Win32.ActiveCfg = Release|Win32
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Release|x64.ActiveCfg = Release|x64
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Debug|Win32.Build.0 = Debug|Win32
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Debug|x64.Build.0 = Debug|x64
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Release|Win32.Build.0 = Release|Win32
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Release|x64.Build.0 = Release|x64
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Debug-DLL|x64.Build.0 = Debug|x64
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Release-DLL|Win32.Build.0 = Release|Win32
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Release-DLL|x64.ActiveCfg = Release|x64
+		{18CF99B5-3C61-EC3D-9509-3C95334C3B88}.Release-DLL|x64.Build.0 = Release|x64
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Debug|Win32.ActiveCfg = Debug|Win32
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Debug|x64.ActiveCfg = Debug|x64
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Release|Win32.ActiveCfg = Release|Win32
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Release|x64.ActiveCfg = Release|x64
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Debug|Win32.Build.0 = Debug|Win32
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Debug|x64.Build.0 = Debug|x64
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Release|Win32.Build.0 = Release|Win32
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Release|x64.Build.0 = Release|x64
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Debug-DLL|x64.Build.0 = Debug|x64
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Release-DLL|Win32.Build.0 = Release|Win32
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Release-DLL|x64.ActiveCfg = Release|x64
+		{14511310-FAE4-C287-31DC-D6F456D10AF3}.Release-DLL|x64.Build.0 = Release|x64
 		{AFC88484-3A2E-32BC-25B2-23DF741D4F3D}.Debug|Win32.ActiveCfg = Debug|Win32
 		{AFC88484-3A2E-32BC-25B2-23DF741D4F3D}.Debug|x64.ActiveCfg = Debug|x64
 		{AFC88484-3A2E-32BC-25B2-23DF741D4F3D}.Release|Win32.ActiveCfg = Release|Win32
@@ -1679,6 +1802,22 @@
 		{6B29F634-1277-74B8-47F6-78756190BA7B}.Release-DLL|Win32.Build.0 = Release|Win32
 		{6B29F634-1277-74B8-47F6-78756190BA7B}.Release-DLL|x64.ActiveCfg = Release|x64
 		{6B29F634-1277-74B8-47F6-78756190BA7B}.Release-DLL|x64.Build.0 = Release|x64
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Debug|Win32.ActiveCfg = Debug|Win32
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Debug|x64.ActiveCfg = Debug|x64
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Release|Win32.ActiveCfg = Release|Win32
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Release|x64.ActiveCfg = Release|x64
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Debug|Win32.Build.0 = Debug|Win32
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Debug|x64.Build.0 = Debug|x64
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Release|Win32.Build.0 = Release|Win32
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Release|x64.Build.0 = Release|x64
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Debug-DLL|x64.Build.0 = Debug|x64
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Release-DLL|Win32.Build.0 = Release|Win32
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Release-DLL|x64.ActiveCfg = Release|x64
+		{C237D1E4-8825-80BA-1FC3-5E147E53E96E}.Release-DLL|x64.Build.0 = Release|x64
 		{5AFE7D17-A4A7-D68E-4491-CBC852F9D2A0}.Debug|Win32.ActiveCfg = Debug|Win32
 		{5AFE7D17-A4A7-D68E-4491-CBC852F9D2A0}.Debug|x64.ActiveCfg = Debug|x64
 		{5AFE7D17-A4A7-D68E-4491-CBC852F9D2A0}.Release|Win32.ActiveCfg = Release|Win32
@@ -1727,6 +1866,22 @@
 		{AF9D0EB2-2A53-B815-3A63-E82C7F91DB29}.Release-DLL|Win32.Build.0 = Release|Win32
 		{AF9D0EB2-2A53-B815-3A63-E82C7F91DB29}.Release-DLL|x64.ActiveCfg = Release|x64
 		{AF9D0EB2-2A53-B815-3A63-E82C7F91DB29}.Release-DLL|x64.Build.0 = Release|x64
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Debug|Win32.ActiveCfg = Debug|Win32
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Debug|x64.ActiveCfg = Debug|x64
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Release|Win32.ActiveCfg = Release|Win32
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Release|x64.ActiveCfg = Release|x64
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Debug|Win32.Build.0 = Debug|Win32
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Debug|x64.Build.0 = Debug|x64
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Release|Win32.Build.0 = Release|Win32
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Release|x64.Build.0 = Release|x64
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Debug-DLL|x64.Build.0 = Debug|x64
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Release-DLL|Win32.Build.0 = Release|Win32
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Release-DLL|x64.ActiveCfg = Release|x64
+		{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}.Release-DLL|x64.Build.0 = Release|x64
 		{F7B6FE68-E847-D7CA-4062-E737E542BCC3}.Debug|Win32.ActiveCfg = Debug|Win32
 		{F7B6FE68-E847-D7CA-4062-E737E542BCC3}.Debug|x64.ActiveCfg = Debug|x64
 		{F7B6FE68-E847-D7CA-4062-E737E542BCC3}.Release|Win32.ActiveCfg = Release|Win32
@@ -1871,6 +2026,22 @@
 		{A635DE99-B131-CA00-2D3B-8691D60B76C2}.Release-DLL|Win32.Build.0 = Release|Win32
 		{A635DE99-B131-CA00-2D3B-8691D60B76C2}.Release-DLL|x64.ActiveCfg = Release|x64
 		{A635DE99-B131-CA00-2D3B-8691D60B76C2}.Release-DLL|x64.Build.0 = Release|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug|Win32.ActiveCfg = Debug|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug|x64.ActiveCfg = Debug|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release|Win32.ActiveCfg = Release|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release|x64.ActiveCfg = Release|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug|Win32.Build.0 = Debug|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug|x64.Build.0 = Debug|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release|Win32.Build.0 = Release|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release|x64.Build.0 = Release|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug-DLL|x64.Build.0 = Debug|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release-DLL|Win32.Build.0 = Release|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release-DLL|x64.ActiveCfg = Release|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release-DLL|x64.Build.0 = Release|x64
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}.Debug|Win32.ActiveCfg = Debug|Win32
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}.Debug|x64.ActiveCfg = Debug|x64
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}.Release|Win32.ActiveCfg = Release|Win32
@@ -2015,6 +2186,38 @@
 		{38797EE3-62CC-3CBF-18D5-009ED6DD0BEC}.Release-DLL|Win32.Build.0 = Release|Win32
 		{38797EE3-62CC-3CBF-18D5-009ED6DD0BEC}.Release-DLL|x64.ActiveCfg = Release|x64
 		{38797EE3-62CC-3CBF-18D5-009ED6DD0BEC}.Release-DLL|x64.Build.0 = Release|x64
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Debug|Win32.ActiveCfg = Debug|Win32
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Debug|x64.ActiveCfg = Debug|x64
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Release|Win32.ActiveCfg = Release|Win32
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Release|x64.ActiveCfg = Release|x64
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Debug|Win32.Build.0 = Debug|Win32
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Debug|x64.Build.0 = Debug|x64
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Release|Win32.Build.0 = Release|Win32
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Release|x64.Build.0 = Release|x64
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Debug-DLL|x64.Build.0 = Debug|x64
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Release-DLL|Win32.Build.0 = Release|Win32
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Release-DLL|x64.ActiveCfg = Release|x64
+		{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}.Release-DLL|x64.Build.0 = Release|x64
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Debug|Win32.ActiveCfg = Debug|Win32
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Debug|x64.ActiveCfg = Debug|x64
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Release|Win32.ActiveCfg = Release|Win32
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Release|x64.ActiveCfg = Release|x64
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Debug|Win32.Build.0 = Debug|Win32
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Debug|x64.Build.0 = Debug|x64
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Release|Win32.Build.0 = Release|Win32
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Release|x64.Build.0 = Release|x64
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Debug-DLL|x64.Build.0 = Debug|x64
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Release-DLL|Win32.Build.0 = Release|Win32
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Release-DLL|x64.ActiveCfg = Release|x64
+		{8313AE17-FCFA-8110-95C7-7AF2F814D188}.Release-DLL|x64.Build.0 = Release|x64
 		{E679773D-DE89-AEBB-9787-59019989B825}.Debug|Win32.ActiveCfg = Debug|Win32
 		{E679773D-DE89-AEBB-9787-59019989B825}.Debug|x64.ActiveCfg = Debug|x64
 		{E679773D-DE89-AEBB-9787-59019989B825}.Release|Win32.ActiveCfg = Release|Win32
@@ -2607,38 +2810,70 @@
 		{EEBEFA75-C625-C823-FE96-9AD64887B57D}.Release-DLL|Win32.Build.0 = Release|Win32
 		{EEBEFA75-C625-C823-FE96-9AD64887B57D}.Release-DLL|x64.ActiveCfg = Release|x64
 		{EEBEFA75-C625-C823-FE96-9AD64887B57D}.Release-DLL|x64.Build.0 = Release|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug|Win32.ActiveCfg = Debug|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug|x64.ActiveCfg = Debug|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release|Win32.ActiveCfg = Release|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release|x64.ActiveCfg = Release|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug|Win32.Build.0 = Debug|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug|x64.Build.0 = Debug|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release|Win32.Build.0 = Release|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release|x64.Build.0 = Release|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug-DLL|Win32.Build.0 = Debug|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug-DLL|x64.ActiveCfg = Debug|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Debug-DLL|x64.Build.0 = Debug|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release-DLL|Win32.ActiveCfg = Release|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release-DLL|Win32.Build.0 = Release|Win32
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release-DLL|x64.ActiveCfg = Release|x64
-		{679EA55C-7399-53E8-79F0-82FBDB3DDE07}.Release-DLL|x64.Build.0 = Release|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug|Win32.ActiveCfg = Debug|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug|x64.ActiveCfg = Debug|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release|Win32.ActiveCfg = Release|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release|x64.ActiveCfg = Release|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug|Win32.Build.0 = Debug|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug|x64.Build.0 = Debug|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release|Win32.Build.0 = Release|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release|x64.Build.0 = Release|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug-DLL|Win32.Build.0 = Debug|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug-DLL|x64.ActiveCfg = Debug|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Debug-DLL|x64.Build.0 = Debug|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release-DLL|Win32.ActiveCfg = Release|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release-DLL|Win32.Build.0 = Release|Win32
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release-DLL|x64.ActiveCfg = Release|x64
-		{B107130E-EA33-C114-9CB6-78A18C929F64}.Release-DLL|x64.Build.0 = Release|x64
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Debug|Win32.ActiveCfg = Debug|Win32
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Debug|x64.ActiveCfg = Debug|x64
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Release|Win32.ActiveCfg = Release|Win32
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Release|x64.ActiveCfg = Release|x64
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Debug|Win32.Build.0 = Debug|Win32
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Debug|x64.Build.0 = Debug|x64
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Release|Win32.Build.0 = Release|Win32
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Release|x64.Build.0 = Release|x64
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Debug-DLL|x64.Build.0 = Debug|x64
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Release-DLL|Win32.Build.0 = Release|Win32
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Release-DLL|x64.ActiveCfg = Release|x64
+		{58EA8DAE-6E50-45A3-0CCC-5165D824380E}.Release-DLL|x64.Build.0 = Release|x64
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Debug|Win32.ActiveCfg = Debug|Win32
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Debug|x64.ActiveCfg = Debug|x64
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Release|Win32.ActiveCfg = Release|Win32
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Release|x64.ActiveCfg = Release|x64
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Debug|Win32.Build.0 = Debug|Win32
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Debug|x64.Build.0 = Debug|x64
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Release|Win32.Build.0 = Release|Win32
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Release|x64.Build.0 = Release|x64
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Debug-DLL|x64.Build.0 = Debug|x64
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Release-DLL|Win32.Build.0 = Release|Win32
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Release-DLL|x64.ActiveCfg = Release|x64
+		{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}.Release-DLL|x64.Build.0 = Release|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug|Win32.ActiveCfg = Debug|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug|x64.ActiveCfg = Debug|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release|Win32.ActiveCfg = Release|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release|x64.ActiveCfg = Release|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug|Win32.Build.0 = Debug|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug|x64.Build.0 = Debug|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release|Win32.Build.0 = Release|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release|x64.Build.0 = Release|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Debug-DLL|x64.Build.0 = Debug|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release-DLL|Win32.Build.0 = Release|Win32
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release-DLL|x64.ActiveCfg = Release|x64
+		{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}.Release-DLL|x64.Build.0 = Release|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug|Win32.ActiveCfg = Debug|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug|x64.ActiveCfg = Debug|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release|Win32.ActiveCfg = Release|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release|x64.ActiveCfg = Release|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug|Win32.Build.0 = Debug|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug|x64.Build.0 = Debug|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release|Win32.Build.0 = Release|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release|x64.Build.0 = Release|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Debug-DLL|x64.Build.0 = Debug|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release-DLL|Win32.Build.0 = Release|Win32
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release-DLL|x64.ActiveCfg = Release|x64
+		{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}.Release-DLL|x64.Build.0 = Release|x64
 		{0F761FF3-342A-C429-711F-F76181BAA52D}.Debug|Win32.ActiveCfg = Debug|Win32
 		{0F761FF3-342A-C429-711F-F76181BAA52D}.Debug|x64.ActiveCfg = Debug|x64
 		{0F761FF3-342A-C429-711F-F76181BAA52D}.Release|Win32.ActiveCfg = Release|Win32
diff --git a/vsprojects/grpc.sln b/vsprojects/grpc.sln
index 8472091..63be171 100644
--- a/vsprojects/grpc.sln
+++ b/vsprojects/grpc.sln
@@ -22,6 +22,11 @@
         	lib = "False"
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gen_percent_encoding_tables", "vcxproj\.\gen_percent_encoding_tables\gen_percent_encoding_tables.vcxproj", "{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gpr", "vcxproj\.\gpr\gpr.vcxproj", "{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}"
 	ProjectSection(myProperties) = preProject
         	lib = "True"
@@ -66,7 +71,6 @@
 	ProjectSection(ProjectDependencies) = postProject
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 		{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5} = {46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}
-		{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
 	EndProjectSection
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grpc_create_jwt", "vcxproj\.\grpc_create_jwt\grpc_create_jwt.vcxproj", "{77971F8D-F583-3E77-0E3C-6C1FB6B1749C}"
@@ -222,6 +226,22 @@
 		{A635DE99-B131-CA00-2D3B-8691D60B76C2}.Release-DLL|Win32.Build.0 = Release|Win32
 		{A635DE99-B131-CA00-2D3B-8691D60B76C2}.Release-DLL|x64.ActiveCfg = Release|x64
 		{A635DE99-B131-CA00-2D3B-8691D60B76C2}.Release-DLL|x64.Build.0 = Release|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug|Win32.ActiveCfg = Debug|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug|x64.ActiveCfg = Debug|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release|Win32.ActiveCfg = Release|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release|x64.ActiveCfg = Release|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug|Win32.Build.0 = Debug|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug|x64.Build.0 = Debug|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release|Win32.Build.0 = Release|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release|x64.Build.0 = Release|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Debug-DLL|x64.Build.0 = Debug|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release-DLL|Win32.Build.0 = Release|Win32
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release-DLL|x64.ActiveCfg = Release|x64
+		{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}.Release-DLL|x64.Build.0 = Release|x64
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}.Debug|Win32.ActiveCfg = Debug|Win32
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}.Debug|x64.ActiveCfg = Debug|x64
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}.Release|Win32.ActiveCfg = Release|Win32
diff --git a/vsprojects/grpc_protoc_plugins.sln b/vsprojects/grpc_protoc_plugins.sln
index 2e55720..c1d0ea2 100644
--- a/vsprojects/grpc_protoc_plugins.sln
+++ b/vsprojects/grpc_protoc_plugins.sln
@@ -35,6 +35,14 @@
 		{B6E81D84-2ACB-41B8-8781-493A944C7817} = {B6E81D84-2ACB-41B8-8781-493A944C7817}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grpc_php_plugin", "vcxproj\.\grpc_php_plugin\grpc_php_plugin.vcxproj", "{2C5F74B5-2F1E-A7A7-45EA-250AF73A1CEC}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+	ProjectSection(ProjectDependencies) = postProject
+		{B6E81D84-2ACB-41B8-8781-493A944C7817} = {B6E81D84-2ACB-41B8-8781-493A944C7817}
+	EndProjectSection
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grpc_plugin_support", "vcxproj\.\grpc_plugin_support\grpc_plugin_support.vcxproj", "{B6E81D84-2ACB-41B8-8781-493A944C7817}"
 	ProjectSection(myProperties) = preProject
         	lib = "True"
@@ -96,6 +104,14 @@
 		{19564640-CEE6-4921-ABA5-676ED79A36F6}.Debug|x64.Build.0 = Debug|x64
 		{19564640-CEE6-4921-ABA5-676ED79A36F6}.Release|Win32.Build.0 = Release|Win32
 		{19564640-CEE6-4921-ABA5-676ED79A36F6}.Release|x64.Build.0 = Release|x64
+		{2C5F74B5-2F1E-A7A7-45EA-250AF73A1CEC}.Debug|Win32.ActiveCfg = Debug|Win32
+		{2C5F74B5-2F1E-A7A7-45EA-250AF73A1CEC}.Debug|x64.ActiveCfg = Debug|x64
+		{2C5F74B5-2F1E-A7A7-45EA-250AF73A1CEC}.Release|Win32.ActiveCfg = Release|Win32
+		{2C5F74B5-2F1E-A7A7-45EA-250AF73A1CEC}.Release|x64.ActiveCfg = Release|x64
+		{2C5F74B5-2F1E-A7A7-45EA-250AF73A1CEC}.Debug|Win32.Build.0 = Debug|Win32
+		{2C5F74B5-2F1E-A7A7-45EA-250AF73A1CEC}.Debug|x64.Build.0 = Debug|x64
+		{2C5F74B5-2F1E-A7A7-45EA-250AF73A1CEC}.Release|Win32.Build.0 = Release|Win32
+		{2C5F74B5-2F1E-A7A7-45EA-250AF73A1CEC}.Release|x64.Build.0 = Release|x64
 		{B6E81D84-2ACB-41B8-8781-493A944C7817}.Debug|Win32.ActiveCfg = Debug|Win32
 		{B6E81D84-2ACB-41B8-8781-493A944C7817}.Debug|x64.ActiveCfg = Debug|x64
 		{B6E81D84-2ACB-41B8-8781-493A944C7817}.Release|Win32.ActiveCfg = Release|Win32
diff --git a/vsprojects/vcxproj/gen_percent_encoding_tables/gen_percent_encoding_tables.vcxproj b/vsprojects/vcxproj/gen_percent_encoding_tables/gen_percent_encoding_tables.vcxproj
new file mode 100644
index 0000000..446b412
--- /dev/null
+++ b/vsprojects/vcxproj/gen_percent_encoding_tables/gen_percent_encoding_tables.vcxproj
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{95D6E277-5ED9-EBDB-3DB8-19C610D2C6F5}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>gen_percent_encoding_tables</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>gen_percent_encoding_tables</TargetName>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\tools\codegen\core\gen_percent_encoding_tables.c">
+    </ClCompile>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/gen_percent_encoding_tables/gen_percent_encoding_tables.vcxproj.filters b/vsprojects/vcxproj/gen_percent_encoding_tables/gen_percent_encoding_tables.vcxproj.filters
new file mode 100644
index 0000000..a787887
--- /dev/null
+++ b/vsprojects/vcxproj/gen_percent_encoding_tables/gen_percent_encoding_tables.vcxproj.filters
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\tools\codegen\core\gen_percent_encoding_tables.c">
+      <Filter>tools\codegen\core</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="tools">
+      <UniqueIdentifier>{e587d5b5-125f-1c73-e004-3c5659aa666b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="tools\codegen">
+      <UniqueIdentifier>{0e90891e-2dd7-433f-2e97-b8495275cc10}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="tools\codegen\core">
+      <UniqueIdentifier>{194d6b8d-bf65-b581-90a4-13447dbfa951}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/gpr/gpr.vcxproj b/vsprojects/vcxproj/gpr/gpr.vcxproj
index db8594e..60772c7 100644
--- a/vsprojects/vcxproj/gpr/gpr.vcxproj
+++ b/vsprojects/vcxproj/gpr/gpr.vcxproj
@@ -175,27 +175,26 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\support\tls_msvc.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\support\tls_pthread.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\support\useful.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\profiling\timers.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\backoff.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\block_annotate.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\env.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\mpscq.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\murmur_hash.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\percent_encoding.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\stack_lockfree.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\string.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\string_windows.h" />
@@ -244,8 +243,12 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\support\log_windows.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\support\mpscq.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\support\murmur_hash.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\support\percent_encoding.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\support\slice.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\support\slice_buffer.c">
diff --git a/vsprojects/vcxproj/gpr/gpr.vcxproj.filters b/vsprojects/vcxproj/gpr/gpr.vcxproj.filters
index 9bab373..f2db937 100644
--- a/vsprojects/vcxproj/gpr/gpr.vcxproj.filters
+++ b/vsprojects/vcxproj/gpr/gpr.vcxproj.filters
@@ -61,9 +61,15 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\support\log_windows.c">
       <Filter>src\core\lib\support</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\support\mpscq.c">
+      <Filter>src\core\lib\support</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\support\murmur_hash.c">
       <Filter>src\core\lib\support</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\support\percent_encoding.c">
+      <Filter>src\core\lib\support</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\support\slice.c">
       <Filter>src\core\lib\support</Filter>
     </ClCompile>
@@ -222,9 +228,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\support\useful.h">
       <Filter>include\grpc\support</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -237,7 +240,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h">
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h">
@@ -246,9 +249,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -261,9 +261,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\profiling\timers.h">
@@ -278,9 +275,15 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\env.h">
       <Filter>src\core\lib\support</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\mpscq.h">
+      <Filter>src\core\lib\support</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\murmur_hash.h">
       <Filter>src\core\lib\support</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\percent_encoding.h">
+      <Filter>src\core\lib\support</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\support\stack_lockfree.h">
       <Filter>src\core\lib\support</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj b/vsprojects/vcxproj/grpc++/grpc++.vcxproj
index cb9e41e..6bb9a61 100644
--- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj
+++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj
@@ -328,6 +328,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\stub_options.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync.h" />
@@ -335,27 +336,23 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_no_cxx11.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_stream.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\core_codegen.h" />
@@ -363,10 +360,13 @@
     <ClInclude Include="$(SolutionDir)\..\src\cpp\common\secure_auth_context.h" />
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\secure_server_credentials.h" />
     <ClInclude Include="$(SolutionDir)\..\src\cpp\client\create_channel_internal.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\cpp\common\channel_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\dynamic_thread_pool.h" />
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\thread_pool_interface.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\insecure_credentials.cc">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\secure_credentials.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\auth_property_iterator.cc">
@@ -377,9 +377,11 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\secure_create_auth_context.cc">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\insecure_server_credentials.cc">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\secure_server_credentials.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\channel.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\channel_cc.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\client_context.cc">
     </ClCompile>
@@ -389,15 +391,15 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\create_channel_posix.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\credentials.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\credentials_cc.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\generic_stub.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\insecure_credentials.cc">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\channel_arguments.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\common\completion_queue.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\common\channel_filter.cc">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\common\completion_queue_cc.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\core_codegen.cc">
     </ClCompile>
@@ -409,27 +411,25 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\dynamic_thread_pool.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\insecure_server_credentials.cc">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server.cc">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_builder.cc">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_cc.cc">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_context.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_credentials.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_posix.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\byte_buffer.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\byte_buffer_cc.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\slice.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\slice_cc.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\util\status.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\util\string_ref.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\time.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\time_cc.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\codegen\codegen_init.cc">
     </ClCompile>
diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
index a905118..761424c 100644
--- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
@@ -1,6 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\insecure_credentials.cc">
+      <Filter>src\cpp\client</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\secure_credentials.cc">
       <Filter>src\cpp\client</Filter>
     </ClCompile>
@@ -16,10 +19,13 @@
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\secure_create_auth_context.cc">
       <Filter>src\cpp\common</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\insecure_server_credentials.cc">
+      <Filter>src\cpp\server</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\secure_server_credentials.cc">
       <Filter>src\cpp\server</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\channel.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\channel_cc.cc">
       <Filter>src\cpp\client</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\client_context.cc">
@@ -34,19 +40,19 @@
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\create_channel_posix.cc">
       <Filter>src\cpp\client</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\credentials.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\credentials_cc.cc">
       <Filter>src\cpp\client</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\generic_stub.cc">
       <Filter>src\cpp\client</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\insecure_credentials.cc">
-      <Filter>src\cpp\client</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\channel_arguments.cc">
       <Filter>src\cpp\common</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\common\completion_queue.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\common\channel_filter.cc">
+      <Filter>src\cpp\common</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\common\completion_queue_cc.cc">
       <Filter>src\cpp\common</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\core_codegen.cc">
@@ -64,15 +70,12 @@
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\dynamic_thread_pool.cc">
       <Filter>src\cpp\server</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\insecure_server_credentials.cc">
-      <Filter>src\cpp\server</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server.cc">
-      <Filter>src\cpp\server</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_builder.cc">
       <Filter>src\cpp\server</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_cc.cc">
+      <Filter>src\cpp\server</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_context.cc">
       <Filter>src\cpp\server</Filter>
     </ClCompile>
@@ -82,10 +85,10 @@
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_posix.cc">
       <Filter>src\cpp\server</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\byte_buffer.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\byte_buffer_cc.cc">
       <Filter>src\cpp\util</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\slice.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\slice_cc.cc">
       <Filter>src\cpp\util</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\util\status.cc">
@@ -94,7 +97,7 @@
     <ClCompile Include="$(SolutionDir)\..\src\cpp\util\string_ref.cc">
       <Filter>src\cpp\util</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\time.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\time_cc.cc">
       <Filter>src\cpp\util</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\codegen\codegen_init.cc">
@@ -312,6 +315,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
@@ -333,9 +339,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -354,9 +357,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -369,7 +369,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h">
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h">
@@ -378,9 +378,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -393,9 +390,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\core_codegen.h">
@@ -413,6 +407,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\cpp\client\create_channel_internal.h">
       <Filter>src\cpp\client</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\cpp\common\channel_filter.h">
+      <Filter>src\cpp\common</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\dynamic_thread_pool.h">
       <Filter>src\cpp\server</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc++_reflection/grpc++_reflection.vcxproj b/vsprojects/vcxproj/grpc++_reflection/grpc++_reflection.vcxproj
index 7c3e63b..2301281 100644
--- a/vsprojects/vcxproj/grpc++_reflection/grpc++_reflection.vcxproj
+++ b/vsprojects/vcxproj/grpc++_reflection/grpc++_reflection.vcxproj
@@ -174,6 +174,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\stub_options.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync.h" />
@@ -181,27 +182,23 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_no_cxx11.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_stream.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\config_protobuf.h" />
   </ItemGroup>
   <ItemGroup>
diff --git a/vsprojects/vcxproj/grpc++_reflection/grpc++_reflection.vcxproj.filters b/vsprojects/vcxproj/grpc++_reflection/grpc++_reflection.vcxproj.filters
index d9cd49c..540e1c6 100644
--- a/vsprojects/vcxproj/grpc++_reflection/grpc++_reflection.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++_reflection/grpc++_reflection.vcxproj.filters
@@ -96,6 +96,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
@@ -117,9 +120,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -138,9 +138,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -153,7 +150,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h">
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h">
@@ -162,9 +159,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -177,9 +171,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\config_protobuf.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc++_test_config/grpc++_test_config.vcxproj b/vsprojects/vcxproj/grpc++_test_config/grpc++_test_config.vcxproj
index 5eab788..cf07e21 100644
--- a/vsprojects/vcxproj/grpc++_test_config/grpc++_test_config.vcxproj
+++ b/vsprojects/vcxproj/grpc++_test_config/grpc++_test_config.vcxproj
@@ -150,7 +150,7 @@
     <ClInclude Include="$(SolutionDir)\..\test\cpp\util\test_config.h" />
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\test_config.cc">
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\test_config_cc.cc">
     </ClCompile>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/vsprojects/vcxproj/grpc++_test_config/grpc++_test_config.vcxproj.filters b/vsprojects/vcxproj/grpc++_test_config/grpc++_test_config.vcxproj.filters
index b4d4134..73e3614 100644
--- a/vsprojects/vcxproj/grpc++_test_config/grpc++_test_config.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++_test_config/grpc++_test_config.vcxproj.filters
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\test_config.cc">
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\test_config_cc.cc">
       <Filter>test\cpp\util</Filter>
     </ClCompile>
   </ItemGroup>
diff --git a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj
index d0fca9b..17e9199 100644
--- a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj
+++ b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj
@@ -170,6 +170,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\stub_options.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync.h" />
@@ -177,29 +178,27 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_no_cxx11.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_stream.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\proto_utils.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\config_protobuf.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\thrift_serializer.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\thrift_utils.h" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\test\cpp\end2end\test_service_impl.h" />
diff --git a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters
index cc98c60..25a1752 100644
--- a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters
@@ -102,6 +102,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
@@ -123,9 +126,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -144,9 +144,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -159,7 +156,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h">
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h">
@@ -168,9 +165,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -183,15 +177,18 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\proto_utils.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\config_protobuf.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\thrift_serializer.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\thrift_utils.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\test\cpp\end2end\test_service_impl.h">
diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
index 03be485..02e6592 100644
--- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
@@ -328,6 +328,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\stub_options.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync.h" />
@@ -335,37 +336,38 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_no_cxx11.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_stream.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\src\cpp\client\create_channel_internal.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\cpp\common\channel_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\dynamic_thread_pool.h" />
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\thread_pool_interface.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\insecure_credentials.cc">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\insecure_create_auth_context.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\channel.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\insecure_server_credentials.cc">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\channel_cc.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\client_context.cc">
     </ClCompile>
@@ -375,15 +377,15 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\create_channel_posix.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\credentials.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\credentials_cc.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\generic_stub.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\insecure_credentials.cc">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\channel_arguments.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\common\completion_queue.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\common\channel_filter.cc">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\common\completion_queue_cc.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\core_codegen.cc">
     </ClCompile>
@@ -395,27 +397,25 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\dynamic_thread_pool.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\insecure_server_credentials.cc">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server.cc">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_builder.cc">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_cc.cc">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_context.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_credentials.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_posix.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\byte_buffer.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\byte_buffer_cc.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\slice.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\slice_cc.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\util\status.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\util\string_ref.cc">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\time.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\time_cc.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\codegen\codegen_init.cc">
     </ClCompile>
@@ -427,9 +427,6 @@
     <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_unsecure\grpc_unsecure.vcxproj">
       <Project>{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}</Project>
     </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
-      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
-    </ProjectReference>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
index ba99bc5..ff1a0e9 100644
--- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
@@ -1,10 +1,16 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\insecure_credentials.cc">
+      <Filter>src\cpp\client</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\insecure_create_auth_context.cc">
       <Filter>src\cpp\common</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\channel.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\insecure_server_credentials.cc">
+      <Filter>src\cpp\server</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\channel_cc.cc">
       <Filter>src\cpp\client</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\client_context.cc">
@@ -19,19 +25,19 @@
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\create_channel_posix.cc">
       <Filter>src\cpp\client</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\credentials.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\credentials_cc.cc">
       <Filter>src\cpp\client</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\client\generic_stub.cc">
       <Filter>src\cpp\client</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\client\insecure_credentials.cc">
-      <Filter>src\cpp\client</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\channel_arguments.cc">
       <Filter>src\cpp\common</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\common\completion_queue.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\common\channel_filter.cc">
+      <Filter>src\cpp\common</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\common\completion_queue_cc.cc">
       <Filter>src\cpp\common</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\common\core_codegen.cc">
@@ -49,15 +55,12 @@
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\dynamic_thread_pool.cc">
       <Filter>src\cpp\server</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\insecure_server_credentials.cc">
-      <Filter>src\cpp\server</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server.cc">
-      <Filter>src\cpp\server</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_builder.cc">
       <Filter>src\cpp\server</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_cc.cc">
+      <Filter>src\cpp\server</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_context.cc">
       <Filter>src\cpp\server</Filter>
     </ClCompile>
@@ -67,10 +70,10 @@
     <ClCompile Include="$(SolutionDir)\..\src\cpp\server\server_posix.cc">
       <Filter>src\cpp\server</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\byte_buffer.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\byte_buffer_cc.cc">
       <Filter>src\cpp\util</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\slice.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\slice_cc.cc">
       <Filter>src\cpp\util</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\util\status.cc">
@@ -79,7 +82,7 @@
     <ClCompile Include="$(SolutionDir)\..\src\cpp\util\string_ref.cc">
       <Filter>src\cpp\util</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\time.cc">
+    <ClCompile Include="$(SolutionDir)\..\src\cpp\util\time_cc.cc">
       <Filter>src\cpp\util</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\cpp\codegen\codegen_init.cc">
@@ -297,6 +300,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
@@ -318,9 +324,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -339,9 +342,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -354,7 +354,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h">
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h">
@@ -363,9 +363,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -378,14 +375,14 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\src\cpp\client\create_channel_internal.h">
       <Filter>src\cpp\client</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\cpp\common\channel_filter.h">
+      <Filter>src\cpp\common</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\dynamic_thread_pool.h">
       <Filter>src\cpp\server</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj
index 33485d3..965d06d 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj
@@ -272,30 +272,26 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\compression.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security_constants.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\status.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security_constants.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\census.h" />
   </ItemGroup>
   <ItemGroup>
@@ -305,9 +301,11 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\compress_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\connected_channel.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\context.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\debug\trace.h" />
@@ -315,6 +313,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\http\httpcli.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\http\parser.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\combiner.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h" />
@@ -376,6 +375,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport_impl.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\bin_decoder.h" />
@@ -397,7 +397,6 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\internal.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\status_conversion.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\timeout_encoding.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\varint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\alpn\alpn.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\context\security_context.h" />
@@ -426,8 +425,8 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\tsi\transport_security_interface.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\client_channel.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\client_channel_factory.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\client_config.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\connector.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\http_connect_handshaker.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\initial_connect_string.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\lb_policy.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\lb_policy_factory.h" />
@@ -436,10 +435,11 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\resolver.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_factory.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_registry.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_result.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_call_holder.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_index.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\uri_parser.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\proto\grpc\lb\v1\load_balancer.pb.h" />
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb.h" />
@@ -449,12 +449,16 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\load_reporting\load_reporting.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\load_reporting\load_reporting_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\aggregation.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\base_resources.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\census_interface.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\census_rpc_stats.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\gen\census.pb.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\gen\trace_context.pb.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\grpc_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\mlog.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\resource.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\rpc_metric_id.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\trace_context.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\init.c">
@@ -469,12 +473,16 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\connected_channel.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.c">
@@ -489,6 +497,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\combiner.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair_posix.c">
@@ -631,6 +641,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport_op_string.c">
@@ -675,8 +687,6 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\timeout_encoding.c">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\varint.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\writing.c">
@@ -747,14 +757,14 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\client_channel_factory.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\client_config.c">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\client_config_plugin.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\connector.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\default_initial_connect_string.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\http_connect_handshaker.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\initial_connect_string.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\lb_policy.c">
@@ -771,9 +781,9 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_registry.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_result.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_call_holder.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_index.c">
     </ClCompile>
@@ -787,6 +797,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\insecure\channel_create_posix.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\proto\grpc\lb\v1\load_balancer.pb.c">
@@ -809,10 +821,14 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\load_reporting\load_reporting_filter.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\base_resources.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\context.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\gen\census.pb.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\gen\trace_context.pb.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\grpc_context.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\grpc_filter.c">
@@ -827,6 +843,10 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\placeholders.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\resource.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\trace_context.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\tracing.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\plugin_registry\grpc_plugin_registry.c">
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
index a21d873..dddd4ec 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
@@ -19,6 +19,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\connected_channel.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
@@ -28,6 +31,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
       <Filter>src\core\lib\compression</Filter>
     </ClCompile>
@@ -49,6 +55,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.c">
       <Filter>src\core\lib\iomgr</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\combiner.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.c">
       <Filter>src\core\lib\iomgr</Filter>
     </ClCompile>
@@ -262,6 +271,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
@@ -328,9 +340,6 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.c">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\timeout_encoding.c">
-      <Filter>src\core\ext\transport\chttp2\transport</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\varint.c">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClCompile>
@@ -436,9 +445,6 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\client_channel_factory.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\client_config.c">
-      <Filter>src\core\ext\client_config</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\client_config_plugin.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
@@ -448,6 +454,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\default_initial_connect_string.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\http_connect_handshaker.c">
+      <Filter>src\core\ext\client_config</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\initial_connect_string.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
@@ -472,10 +481,10 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_registry.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_result.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_call_holder.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_index.c">
@@ -496,6 +505,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\client\insecure\channel_create_posix.c">
       <Filter>src\core\ext\transport\chttp2\client\insecure</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.c">
+      <Filter>src\core\ext\lb_policy\grpclb</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.c">
       <Filter>src\core\ext\lb_policy\grpclb</Filter>
     </ClCompile>
@@ -529,12 +541,18 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\load_reporting\load_reporting_filter.c">
       <Filter>src\core\ext\load_reporting</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\base_resources.c">
+      <Filter>src\core\ext\census</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\context.c">
       <Filter>src\core\ext\census</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\gen\census.pb.c">
       <Filter>src\core\ext\census\gen</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\gen\trace_context.pb.c">
+      <Filter>src\core\ext\census\gen</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\grpc_context.c">
       <Filter>src\core\ext\census</Filter>
     </ClCompile>
@@ -556,6 +574,12 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\placeholders.c">
       <Filter>src\core\ext\census</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\resource.c">
+      <Filter>src\core\ext\census</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\trace_context.c">
+      <Filter>src\core\ext\census</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\tracing.c">
       <Filter>src\core\ext\census</Filter>
     </ClCompile>
@@ -579,11 +603,11 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_posix.h">
       <Filter>include\grpc</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\status.h">
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security_constants.h">
       <Filter>include\grpc</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\status.h">
+      <Filter>include\grpc</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h">
       <Filter>include\grpc\impl\codegen</Filter>
@@ -603,9 +627,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -618,7 +639,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h">
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h">
@@ -627,9 +648,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -642,15 +660,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security.h">
       <Filter>include\grpc</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security_constants.h">
-      <Filter>include\grpc</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\census.h">
       <Filter>include\grpc</Filter>
     </ClInclude>
@@ -674,6 +686,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\context.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
@@ -683,6 +698,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h">
       <Filter>src\core\lib\compression</Filter>
     </ClInclude>
@@ -704,6 +722,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\combiner.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
@@ -887,6 +908,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
@@ -950,9 +974,6 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.h">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\timeout_encoding.h">
-      <Filter>src\core\ext\transport\chttp2\transport</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\varint.h">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClInclude>
@@ -1037,10 +1058,10 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\client_channel_factory.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\client_config.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\connector.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\connector.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\http_connect_handshaker.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\initial_connect_string.h">
@@ -1067,10 +1088,10 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_registry.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_result.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_call_holder.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_index.h">
@@ -1079,6 +1100,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\uri_parser.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.h">
+      <Filter>src\core\ext\lb_policy\grpclb</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.h">
       <Filter>src\core\ext\lb_policy\grpclb</Filter>
     </ClInclude>
@@ -1106,6 +1130,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\aggregation.h">
       <Filter>src\core\ext\census</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\base_resources.h">
+      <Filter>src\core\ext\census</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\census_interface.h">
       <Filter>src\core\ext\census</Filter>
     </ClInclude>
@@ -1115,15 +1142,24 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\gen\census.pb.h">
       <Filter>src\core\ext\census\gen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\gen\trace_context.pb.h">
+      <Filter>src\core\ext\census\gen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\grpc_filter.h">
       <Filter>src\core\ext\census</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\mlog.h">
       <Filter>src\core\ext\census</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\resource.h">
+      <Filter>src\core\ext\census</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\rpc_metric_id.h">
       <Filter>src\core\ext\census</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\trace_context.h">
+      <Filter>src\core\ext\census</Filter>
+    </ClInclude>
   </ItemGroup>
 
   <ItemGroup>
diff --git a/vsprojects/vcxproj/grpc_cli_libs/grpc_cli_libs.vcxproj b/vsprojects/vcxproj/grpc_cli_libs/grpc_cli_libs.vcxproj
index d25c692..2790884 100644
--- a/vsprojects/vcxproj/grpc_cli_libs/grpc_cli_libs.vcxproj
+++ b/vsprojects/vcxproj/grpc_cli_libs/grpc_cli_libs.vcxproj
@@ -148,16 +148,26 @@
 
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\test\cpp\util\cli_call.h" />
+    <ClInclude Include="$(SolutionDir)\..\test\cpp\util\cli_credentials.h" />
+    <ClInclude Include="$(SolutionDir)\..\test\cpp\util\config_grpc_cli.h" />
+    <ClInclude Include="$(SolutionDir)\..\test\cpp\util\grpc_tool.h" />
     <ClInclude Include="$(SolutionDir)\..\test\cpp\util\proto_file_parser.h" />
     <ClInclude Include="$(SolutionDir)\..\test\cpp\util\proto_reflection_descriptor_database.h" />
+    <ClInclude Include="$(SolutionDir)\..\test\cpp\util\service_describer.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="$(SolutionDir)\..\test\cpp\util\cli_call.cc">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\cli_credentials.cc">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\grpc_tool.cc">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\cpp\util\proto_file_parser.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\cpp\util\proto_reflection_descriptor_database.cc">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\service_describer.cc">
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_reflection\grpc++_reflection.vcxproj">
@@ -166,8 +176,8 @@
     <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++\grpc++.vcxproj">
       <Project>{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}</Project>
     </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_plugin_support\grpc_plugin_support.vcxproj">
-      <Project>{B6E81D84-2ACB-41B8-8781-493A944C7817}</Project>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_config\grpc++_test_config.vcxproj">
+      <Project>{3F7D093D-11F9-C4BC-BEB7-18EB28E3F290}</Project>
     </ProjectReference>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/vsprojects/vcxproj/grpc_cli_libs/grpc_cli_libs.vcxproj.filters b/vsprojects/vcxproj/grpc_cli_libs/grpc_cli_libs.vcxproj.filters
index 4add8ed..d4d6927 100644
--- a/vsprojects/vcxproj/grpc_cli_libs/grpc_cli_libs.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_cli_libs/grpc_cli_libs.vcxproj.filters
@@ -4,23 +4,44 @@
     <ClCompile Include="$(SolutionDir)\..\test\cpp\util\cli_call.cc">
       <Filter>test\cpp\util</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\cli_credentials.cc">
+      <Filter>test\cpp\util</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\grpc_tool.cc">
+      <Filter>test\cpp\util</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\cpp\util\proto_file_parser.cc">
       <Filter>test\cpp\util</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\cpp\util\proto_reflection_descriptor_database.cc">
       <Filter>test\cpp\util</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\service_describer.cc">
+      <Filter>test\cpp\util</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\test\cpp\util\cli_call.h">
       <Filter>test\cpp\util</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\test\cpp\util\cli_credentials.h">
+      <Filter>test\cpp\util</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\test\cpp\util\config_grpc_cli.h">
+      <Filter>test\cpp\util</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\test\cpp\util\grpc_tool.h">
+      <Filter>test\cpp\util</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\test\cpp\util\proto_file_parser.h">
       <Filter>test\cpp\util</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\test\cpp\util\proto_reflection_descriptor_database.h">
       <Filter>test\cpp\util</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\test\cpp\util\service_describer.h">
+      <Filter>test\cpp\util</Filter>
+    </ClInclude>
   </ItemGroup>
 
   <ItemGroup>
diff --git a/vsprojects/vcxproj/grpc_php_plugin/grpc_php_plugin.vcxproj b/vsprojects/vcxproj/grpc_php_plugin/grpc_php_plugin.vcxproj
new file mode 100644
index 0000000..1648ba9
--- /dev/null
+++ b/vsprojects/vcxproj/grpc_php_plugin/grpc_php_plugin.vcxproj
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{2C5F74B5-2F1E-A7A7-45EA-250AF73A1CEC}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\protobuf.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\protoc.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>grpc_php_plugin</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>grpc_php_plugin</TargetName>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\compiler\php_plugin.cc">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_plugin_support\grpc_plugin_support.vcxproj">
+      <Project>{B6E81D84-2ACB-41B8-8781-493A944C7817}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/grpc_php_plugin/grpc_php_plugin.vcxproj.filters b/vsprojects/vcxproj/grpc_php_plugin/grpc_php_plugin.vcxproj.filters
new file mode 100644
index 0000000..e131753
--- /dev/null
+++ b/vsprojects/vcxproj/grpc_php_plugin/grpc_php_plugin.vcxproj.filters
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\compiler\php_plugin.cc">
+      <Filter>src\compiler</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="src">
+      <UniqueIdentifier>{d7fb4039-77f4-10f2-59fe-bb98fb56950a}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\compiler">
+      <UniqueIdentifier>{5560fb58-2ae8-75cc-fbca-e630a50c15bf}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj b/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj
index 9828b8b..05165d6 100644
--- a/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj
+++ b/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj
@@ -161,6 +161,8 @@
     <ClInclude Include="$(SolutionDir)\..\src\compiler\node_generator_helpers.h" />
     <ClInclude Include="$(SolutionDir)\..\src\compiler\objective_c_generator.h" />
     <ClInclude Include="$(SolutionDir)\..\src\compiler\objective_c_generator_helpers.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\compiler\php_generator.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\compiler\php_generator_helpers.h" />
     <ClInclude Include="$(SolutionDir)\..\src\compiler\python_generator.h" />
     <ClInclude Include="$(SolutionDir)\..\src\compiler\ruby_generator.h" />
     <ClInclude Include="$(SolutionDir)\..\src\compiler\ruby_generator_helpers-inl.h" />
@@ -176,6 +178,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\compiler\objective_c_generator.cc">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\compiler\php_generator.cc">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\compiler\python_generator.cc">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\compiler\ruby_generator.cc">
diff --git a/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj.filters b/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj.filters
index 27eb935..c8b8932 100644
--- a/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj.filters
@@ -13,6 +13,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\compiler\objective_c_generator.cc">
       <Filter>src\compiler</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\compiler\php_generator.cc">
+      <Filter>src\compiler</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\compiler\python_generator.cc">
       <Filter>src\compiler</Filter>
     </ClCompile>
@@ -56,6 +59,12 @@
     <ClInclude Include="$(SolutionDir)\..\src\compiler\objective_c_generator_helpers.h">
       <Filter>src\compiler</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\compiler\php_generator.h">
+      <Filter>src\compiler</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\compiler\php_generator_helpers.h">
+      <Filter>src\compiler</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\compiler\python_generator.h">
       <Filter>src\compiler</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
index f0a8f7b..b724c21 100644
--- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
+++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
@@ -147,9 +147,37 @@
   </ItemDefinitionGroup>
 
   <ItemGroup>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\byte_buffer.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\byte_buffer_reader.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\compression.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security_constants.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\status.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h" />
+  </ItemGroup>
+  <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\test\core\end2end\data\ssl_test_data.h" />
     <ClInclude Include="$(SolutionDir)\..\test\core\security\oauth2_utils.h" />
     <ClInclude Include="$(SolutionDir)\..\test\core\end2end\cq_verifier.h" />
+    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\fake_resolver.h" />
+    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\fixtures\http_proxy.h" />
     <ClInclude Include="$(SolutionDir)\..\test\core\end2end\fixtures\proxy.h" />
     <ClInclude Include="$(SolutionDir)\..\test\core\iomgr\endpoint_tests.h" />
     <ClInclude Include="$(SolutionDir)\..\test\core\util\grpc_profiler.h" />
@@ -160,6 +188,89 @@
     <ClInclude Include="$(SolutionDir)\..\test\core\util\port.h" />
     <ClInclude Include="$(SolutionDir)\..\test\core\util\port_server_client.h" />
     <ClInclude Include="$(SolutionDir)\..\test\core\util\slice_splitter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\channel_args.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\channel_stack.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\channel_stack_builder.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\compress_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\connected_channel.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\context.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\debug\trace.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\http\format_request.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\http\httpcli.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\http\parser.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\combiner.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_epoll_linux.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_and_epoll_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\exec_ctx.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr_internal.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\load_file.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\network_status_tracker.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\polling_entity.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_set.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_set_windows.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_windows.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr_utils.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr_windows.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\socket_utils_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\socket_windows.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_client.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_server.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_windows.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\time_averaged_stats.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\timer.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\timer_heap.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\udp_server.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\unix_sockets_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\wakeup_fd_pipe.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\wakeup_fd_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\workqueue.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\workqueue_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\workqueue_windows.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_common.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_reader.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_writer.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\call.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\call_test_only.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\channel.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\channel_init.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\channel_stack_type.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\completion_queue.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\event_string.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\init.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\lame_client.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\server.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\byte_stream.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport_impl.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\data\client_certs.c">
@@ -174,6 +285,10 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\cq_verifier.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fake_resolver.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\http_proxy.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\proxy.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\iomgr\endpoint_tests.c">
@@ -196,6 +311,190 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\util\slice_splitter.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\channel_args.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\channel_stack.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\channel_stack_builder.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\compress_filter.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\connected_channel.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\debug\trace.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\http\format_request.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\http\httpcli.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\http\parser.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\combiner.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair_windows.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\error.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_epoll_linux.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_and_epoll_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\exec_ctx.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr_windows.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\load_file.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\network_status_tracker.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\polling_entity.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_set_windows.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_windows.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address_windows.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr_utils.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\socket_utils_common_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\socket_utils_linux.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\socket_utils_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\socket_windows.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_client_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_client_windows.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_server_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_server_windows.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_windows.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\time_averaged_stats.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\timer.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\timer_heap.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\udp_server.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\unix_sockets_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\unix_sockets_posix_noop.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\wakeup_fd_eventfd.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\wakeup_fd_nospecial.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\wakeup_fd_pipe.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\wakeup_fd_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\workqueue_posix.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\workqueue_windows.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\json\json.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\json\json_reader.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\json\json_string.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\json\json_writer.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\alarm.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\byte_buffer.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\byte_buffer_reader.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\call.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\call_details.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\call_log_batch.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\channel.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\channel_init.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\channel_ping.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\channel_stack_type.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\completion_queue.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\event_string.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\lame_client.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\metadata_array.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\server.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\validate_metadata.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\version.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\byte_stream.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport_op_string.c">
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
index a1d31eb..92806fa 100644
--- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
@@ -19,6 +19,12 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\cq_verifier.c">
       <Filter>test\core\end2end</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fake_resolver.c">
+      <Filter>test\core\end2end</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\http_proxy.c">
+      <Filter>test\core\end2end\fixtures</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\proxy.c">
       <Filter>test\core\end2end\fixtures</Filter>
     </ClCompile>
@@ -52,6 +58,356 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\util\slice_splitter.c">
       <Filter>test\core\util</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\channel_args.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\channel_stack.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\channel_stack_builder.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\compress_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\connected_channel.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
+      <Filter>src\core\lib\compression</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.c">
+      <Filter>src\core\lib\compression</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\debug\trace.c">
+      <Filter>src\core\lib\debug</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\http\format_request.c">
+      <Filter>src\core\lib\http</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\http\httpcli.c">
+      <Filter>src\core\lib\http</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\http\parser.c">
+      <Filter>src\core\lib\http</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\combiner.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair_windows.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\error.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_epoll_linux.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_and_epoll_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\exec_ctx.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr_windows.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\load_file.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\network_status_tracker.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\polling_entity.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_set_windows.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_windows.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address_windows.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr_utils.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\socket_utils_common_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\socket_utils_linux.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\socket_utils_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\socket_windows.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_client_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_client_windows.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_server_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_server_windows.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_windows.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\time_averaged_stats.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\timer.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\timer_heap.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\udp_server.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\unix_sockets_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\unix_sockets_posix_noop.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\wakeup_fd_eventfd.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\wakeup_fd_nospecial.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\wakeup_fd_pipe.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\wakeup_fd_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\workqueue_posix.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\workqueue_windows.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\json\json.c">
+      <Filter>src\core\lib\json</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\json\json_reader.c">
+      <Filter>src\core\lib\json</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\json\json_string.c">
+      <Filter>src\core\lib\json</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\json\json_writer.c">
+      <Filter>src\core\lib\json</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\alarm.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\byte_buffer.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\byte_buffer_reader.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\call.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\call_details.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\call_log_batch.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\channel.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\channel_init.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\channel_ping.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\channel_stack_type.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\completion_queue.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\event_string.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\lame_client.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\metadata_array.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\server.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\validate_metadata.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\version.c">
+      <Filter>src\core\lib\surface</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\byte_stream.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport_op_string.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\byte_buffer.h">
+      <Filter>include\grpc</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\byte_buffer_reader.h">
+      <Filter>include\grpc</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\compression.h">
+      <Filter>include\grpc</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc.h">
+      <Filter>include\grpc</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_posix.h">
+      <Filter>include\grpc</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security_constants.h">
+      <Filter>include\grpc</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\status.h">
+      <Filter>include\grpc</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\test\core\end2end\data\ssl_test_data.h">
@@ -63,6 +419,12 @@
     <ClInclude Include="$(SolutionDir)\..\test\core\end2end\cq_verifier.h">
       <Filter>test\core\end2end</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\fake_resolver.h">
+      <Filter>test\core\end2end</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\fixtures\http_proxy.h">
+      <Filter>test\core\end2end\fixtures</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\test\core\end2end\fixtures\proxy.h">
       <Filter>test\core\end2end\fixtures</Filter>
     </ClInclude>
@@ -93,9 +455,303 @@
     <ClInclude Include="$(SolutionDir)\..\test\core\util\slice_splitter.h">
       <Filter>test\core\util</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\channel_args.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\channel_stack.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\channel_stack_builder.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\compress_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\connected_channel.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\context.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h">
+      <Filter>src\core\lib\compression</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.h">
+      <Filter>src\core\lib\compression</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\debug\trace.h">
+      <Filter>src\core\lib\debug</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\http\format_request.h">
+      <Filter>src\core\lib\http</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\http\httpcli.h">
+      <Filter>src\core\lib\http</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\http\parser.h">
+      <Filter>src\core\lib\http</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\combiner.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_epoll_linux.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_and_epoll_posix.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_posix.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_posix.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\exec_ctx.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\executor.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iocp_windows.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr_internal.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\iomgr_posix.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\load_file.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\network_status_tracker.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\polling_entity.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_set.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_set_windows.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\pollset_windows.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\resolve_address.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr_posix.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr_utils.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\sockaddr_windows.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\socket_utils_posix.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\socket_windows.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_client.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_posix.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_server.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\tcp_windows.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\time_averaged_stats.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\timer.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\timer_heap.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\udp_server.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\unix_sockets_posix.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\wakeup_fd_pipe.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\wakeup_fd_posix.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\workqueue.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\workqueue_posix.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\workqueue_windows.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json.h">
+      <Filter>src\core\lib\json</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_common.h">
+      <Filter>src\core\lib\json</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_reader.h">
+      <Filter>src\core\lib\json</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_writer.h">
+      <Filter>src\core\lib\json</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\call.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\call_test_only.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\channel.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\channel_init.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\channel_stack_type.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\completion_queue.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\event_string.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\init.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\lame_client.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\server.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\byte_stream.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport_impl.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
   </ItemGroup>
 
   <ItemGroup>
+    <Filter Include="include">
+      <UniqueIdentifier>{50129440-aff7-7df7-682c-b9671be19a6f}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="include\grpc">
+      <UniqueIdentifier>{d448b078-95a6-6fca-fe4a-8b44dd71f359}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="include\grpc\impl">
+      <UniqueIdentifier>{314a6801-6fe3-9211-33d8-ecf3332c1151}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="include\grpc\impl\codegen">
+      <UniqueIdentifier>{8e97f1e1-f4d1-a56e-0837-7901778fb3b9}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src">
+      <UniqueIdentifier>{7d107d7c-1da3-9525-3ba1-3a411b552ea8}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core">
+      <UniqueIdentifier>{f7bfac91-5eb2-dea7-4601-6c63edbbf997}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib">
+      <UniqueIdentifier>{f4e8c61e-1ca6-0fdd-7b5e-b7f9a30c9a21}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\channel">
+      <UniqueIdentifier>{1cd1503c-bec0-5ade-c75f-aa25c80975ec}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\compression">
+      <UniqueIdentifier>{09632582-2cc3-5618-d673-65d3884f8ce5}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\debug">
+      <UniqueIdentifier>{2c1a72e9-886e-8082-9d2f-0fc9cb3ab996}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\http">
+      <UniqueIdentifier>{4862ecce-fa07-eb5e-5c05-bfa753c8bfe5}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\iomgr">
+      <UniqueIdentifier>{fc7f488e-08b4-8366-3720-1f7ffaa0b0b3}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\json">
+      <UniqueIdentifier>{89bc8f83-e29a-ddab-8f6b-22df11cdc867}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\surface">
+      <UniqueIdentifier>{7f2b7dca-395f-94dd-c9ad-9a286bd9751e}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\transport">
+      <UniqueIdentifier>{5249e884-ea07-6782-531d-ec622c54b9af}</UniqueIdentifier>
+    </Filter>
     <Filter Include="test">
       <UniqueIdentifier>{a2783de3-4fcf-718d-a859-c2108350ff33}</UniqueIdentifier>
     </Filter>
diff --git a/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj b/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj
index 33860c4..7878683 100644
--- a/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj
@@ -148,6 +148,8 @@
 
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\test\core\end2end\cq_verifier.h" />
+    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\fake_resolver.h" />
+    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\fixtures\http_proxy.h" />
     <ClInclude Include="$(SolutionDir)\..\test\core\end2end\fixtures\proxy.h" />
     <ClInclude Include="$(SolutionDir)\..\test\core\iomgr\endpoint_tests.h" />
     <ClInclude Include="$(SolutionDir)\..\test\core\util\grpc_profiler.h" />
@@ -162,6 +164,10 @@
   <ItemGroup>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\cq_verifier.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fake_resolver.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\http_proxy.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\proxy.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\iomgr\endpoint_tests.c">
diff --git a/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj.filters
index 372bb2a..2b20ab3 100644
--- a/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj.filters
@@ -4,6 +4,12 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\cq_verifier.c">
       <Filter>test\core\end2end</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fake_resolver.c">
+      <Filter>test\core\end2end</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\http_proxy.c">
+      <Filter>test\core\end2end\fixtures</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\proxy.c">
       <Filter>test\core\end2end\fixtures</Filter>
     </ClCompile>
@@ -42,6 +48,12 @@
     <ClInclude Include="$(SolutionDir)\..\test\core\end2end\cq_verifier.h">
       <Filter>test\core\end2end</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\fake_resolver.h">
+      <Filter>test\core\end2end</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\fixtures\http_proxy.h">
+      <Filter>test\core\end2end\fixtures</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\test\core\end2end\fixtures\proxy.h">
       <Filter>test\core\end2end\fixtures</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
index 3a3610d5..b467736 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
@@ -263,28 +263,25 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\compression.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security_constants.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\status.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\census.h" />
   </ItemGroup>
   <ItemGroup>
@@ -294,9 +291,11 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\compress_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\connected_channel.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\context.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\debug\trace.h" />
@@ -304,6 +303,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\http\httpcli.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\http\parser.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\combiner.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h" />
@@ -365,6 +365,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport_impl.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\bin_decoder.h" />
@@ -386,13 +387,12 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\internal.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\status_conversion.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\timeout_encoding.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\varint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\alpn\alpn.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\client_channel.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\client_channel_factory.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\client_config.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\connector.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\http_connect_handshaker.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\initial_connect_string.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\lb_policy.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\lb_policy_factory.h" />
@@ -401,12 +401,13 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\resolver.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_factory.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_registry.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_result.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_call_holder.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_index.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\uri_parser.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\load_reporting\load_reporting.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\load_reporting\load_reporting_filter.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\proto\grpc\lb\v1\load_balancer.pb.h" />
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb.h" />
@@ -414,12 +415,16 @@
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb_decode.h" />
     <ClInclude Include="$(SolutionDir)\..\third_party\nanopb\pb_encode.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\aggregation.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\base_resources.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\census_interface.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\census_rpc_stats.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\gen\census.pb.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\gen\trace_context.pb.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\grpc_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\mlog.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\resource.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\rpc_metric_id.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\trace_context.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\init.c">
@@ -436,12 +441,16 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\connected_channel.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_client_filter.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\message_compress.c">
@@ -456,6 +465,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\combiner.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair_posix.c">
@@ -598,6 +609,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport_op_string.c">
@@ -644,8 +657,6 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\timeout_encoding.c">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\varint.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\writing.c">
@@ -662,14 +673,14 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\client_channel_factory.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\client_config.c">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\client_config_plugin.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\connector.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\default_initial_connect_string.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\http_connect_handshaker.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\initial_connect_string.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\lb_policy.c">
@@ -686,9 +697,9 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_registry.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_result.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_call_holder.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_index.c">
     </ClCompile>
@@ -702,6 +713,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\load_reporting\load_reporting_filter.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\proto\grpc\lb\v1\load_balancer.pb.c">
@@ -716,10 +729,14 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\round_robin\round_robin.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\base_resources.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\context.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\gen\census.pb.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\gen\trace_context.pb.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\grpc_context.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\grpc_filter.c">
@@ -734,6 +751,10 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\placeholders.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\resource.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\trace_context.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\tracing.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\plugin_registry\grpc_unsecure_plugin_registry.c">
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
index bec089e..6ffc1ea 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
@@ -22,6 +22,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\connected_channel.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
@@ -31,6 +34,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.c">
       <Filter>src\core\lib\channel</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.c">
+      <Filter>src\core\lib\channel</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\compression\compression.c">
       <Filter>src\core\lib\compression</Filter>
     </ClCompile>
@@ -52,6 +58,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.c">
       <Filter>src\core\lib\iomgr</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\combiner.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.c">
       <Filter>src\core\lib\iomgr</Filter>
     </ClCompile>
@@ -265,6 +274,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
@@ -334,9 +346,6 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.c">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\timeout_encoding.c">
-      <Filter>src\core\ext\transport\chttp2\transport</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\varint.c">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClCompile>
@@ -361,9 +370,6 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\client_channel_factory.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\client_config.c">
-      <Filter>src\core\ext\client_config</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\client_config_plugin.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
@@ -373,6 +379,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\default_initial_connect_string.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\http_connect_handshaker.c">
+      <Filter>src\core\ext\client_config</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\initial_connect_string.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
@@ -397,10 +406,10 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_registry.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_result.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_call_holder.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.c">
       <Filter>src\core\ext\client_config</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_index.c">
@@ -421,6 +430,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\load_reporting\load_reporting_filter.c">
       <Filter>src\core\ext\load_reporting</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.c">
+      <Filter>src\core\ext\lb_policy\grpclb</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.c">
       <Filter>src\core\ext\lb_policy\grpclb</Filter>
     </ClCompile>
@@ -442,12 +454,18 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\lb_policy\round_robin\round_robin.c">
       <Filter>src\core\ext\lb_policy\round_robin</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\base_resources.c">
+      <Filter>src\core\ext\census</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\context.c">
       <Filter>src\core\ext\census</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\gen\census.pb.c">
       <Filter>src\core\ext\census\gen</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\gen\trace_context.pb.c">
+      <Filter>src\core\ext\census\gen</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\grpc_context.c">
       <Filter>src\core\ext\census</Filter>
     </ClCompile>
@@ -469,6 +487,12 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\placeholders.c">
       <Filter>src\core\ext\census</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\resource.c">
+      <Filter>src\core\ext\census</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\trace_context.c">
+      <Filter>src\core\ext\census</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\census\tracing.c">
       <Filter>src\core\ext\census</Filter>
     </ClCompile>
@@ -492,11 +516,11 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_posix.h">
       <Filter>include\grpc</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\status.h">
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\grpc_security_constants.h">
       <Filter>include\grpc</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\status.h">
+      <Filter>include\grpc</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h">
       <Filter>include\grpc\impl\codegen</Filter>
@@ -516,9 +540,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -531,7 +552,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h">
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h">
@@ -540,9 +561,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -555,9 +573,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\census.h">
       <Filter>include\grpc</Filter>
     </ClInclude>
@@ -581,6 +596,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\context.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\deadline_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\handshaker.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
@@ -590,6 +608,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\http_server_filter.h">
       <Filter>src\core\lib\channel</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\channel\message_size_filter.h">
+      <Filter>src\core\lib\channel</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\compression\algorithm_metadata.h">
       <Filter>src\core\lib\compression</Filter>
     </ClInclude>
@@ -611,6 +632,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\combiner.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
@@ -794,6 +818,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
@@ -857,9 +884,6 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.h">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\timeout_encoding.h">
-      <Filter>src\core\ext\transport\chttp2\transport</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\varint.h">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClInclude>
@@ -872,10 +896,10 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\client_channel_factory.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\client_config.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\connector.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\connector.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\http_connect_handshaker.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\initial_connect_string.h">
@@ -902,10 +926,10 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_registry.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\resolver_result.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_call_holder.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel.h">
       <Filter>src\core\ext\client_config</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\client_config\subchannel_index.h">
@@ -920,6 +944,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\load_reporting\load_reporting_filter.h">
       <Filter>src\core\ext\load_reporting</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\grpclb.h">
+      <Filter>src\core\ext\lb_policy\grpclb</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\lb_policy\grpclb\load_balancer_api.h">
       <Filter>src\core\ext\lb_policy\grpclb</Filter>
     </ClInclude>
@@ -941,6 +968,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\aggregation.h">
       <Filter>src\core\ext\census</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\base_resources.h">
+      <Filter>src\core\ext\census</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\census_interface.h">
       <Filter>src\core\ext\census</Filter>
     </ClInclude>
@@ -950,15 +980,24 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\gen\census.pb.h">
       <Filter>src\core\ext\census\gen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\gen\trace_context.pb.h">
+      <Filter>src\core\ext\census\gen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\grpc_filter.h">
       <Filter>src\core\ext\census</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\mlog.h">
       <Filter>src\core\ext\census</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\resource.h">
+      <Filter>src\core\ext\census</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\rpc_metric_id.h">
       <Filter>src\core\ext\census</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\ext\census\trace_context.h">
+      <Filter>src\core\ext\census</Filter>
+    </ClInclude>
   </ItemGroup>
 
   <ItemGroup>
diff --git a/vsprojects/vcxproj/interop_server_lib/interop_server_lib.vcxproj b/vsprojects/vcxproj/interop_server_lib/interop_server_lib.vcxproj
new file mode 100644
index 0000000..204d3f7
--- /dev/null
+++ b/vsprojects/vcxproj/interop_server_lib/interop_server_lib.vcxproj
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{458DCA09-83B9-5E68-D7E9-118864ECBD94}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>interop_server_lib</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>interop_server_lib</TargetName>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\test.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\test.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\test.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\test.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\interop\interop_server.cc">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\interop_server_helper\interop_server_helper.vcxproj">
+      <Project>{F55BEA2C-B61D-AAFE-CA15-223B8AC0DE5A}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_util\grpc++_test_util.vcxproj">
+      <Project>{0BE77741-552A-929B-A497-4EF7ECE17A64}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
+      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++\grpc++.vcxproj">
+      <Project>{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_config\grpc++_test_config.vcxproj">
+      <Project>{3F7D093D-11F9-C4BC-BEB7-18EB28E3F290}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/interop_server_lib/interop_server_lib.vcxproj.filters b/vsprojects/vcxproj/interop_server_lib/interop_server_lib.vcxproj.filters
new file mode 100644
index 0000000..63fc0f7
--- /dev/null
+++ b/vsprojects/vcxproj/interop_server_lib/interop_server_lib.vcxproj.filters
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.proto">
+      <Filter>src\proto\grpc\testing</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.proto">
+      <Filter>src\proto\grpc\testing</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\test.proto">
+      <Filter>src\proto\grpc\testing</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\interop\interop_server.cc">
+      <Filter>test\cpp\interop</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="src">
+      <UniqueIdentifier>{356c90d7-2dcd-5f6a-d3ca-6461f2597581}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto">
+      <UniqueIdentifier>{70740334-0cbf-ab29-0e1c-f0ffa390d77f}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto\grpc">
+      <UniqueIdentifier>{d581eb6c-94b6-eb79-6b76-d122c13cff3c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto\grpc\testing">
+      <UniqueIdentifier>{27f43e87-cfd9-68cc-179a-fc44046797c4}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test">
+      <UniqueIdentifier>{3402c01e-a9f6-2dd8-6963-03a5774d37f2}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\cpp">
+      <UniqueIdentifier>{656eed4b-782e-224c-6101-8a61c2daa94e}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\cpp\interop">
+      <UniqueIdentifier>{61c0dab5-5c69-82b0-2961-4104445f2e06}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/interop_server_main/interop_server_main.vcxproj b/vsprojects/vcxproj/interop_server_main/interop_server_main.vcxproj
index 18971d6..9fd4d11 100644
--- a/vsprojects/vcxproj/interop_server_main/interop_server_main.vcxproj
+++ b/vsprojects/vcxproj/interop_server_main/interop_server_main.vcxproj
@@ -147,57 +147,12 @@
   </ItemDefinitionGroup>
 
   <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.pb.cc">
-    </ClCompile>
-    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.pb.h">
-    </ClInclude>
-    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.grpc.pb.cc">
-    </ClCompile>
-    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.grpc.pb.h">
-    </ClInclude>
-    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.pb.cc">
-    </ClCompile>
-    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.pb.h">
-    </ClInclude>
-    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.grpc.pb.cc">
-    </ClCompile>
-    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.grpc.pb.h">
-    </ClInclude>
-    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\test.pb.cc">
-    </ClCompile>
-    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\test.pb.h">
-    </ClInclude>
-    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\test.grpc.pb.cc">
-    </ClCompile>
-    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\test.grpc.pb.h">
-    </ClInclude>
-    <ClCompile Include="$(SolutionDir)\..\test\cpp\interop\interop_server.cc">
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\interop\interop_server_bootstrap.cc">
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\interop_server_helper\interop_server_helper.vcxproj">
-      <Project>{F55BEA2C-B61D-AAFE-CA15-223B8AC0DE5A}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_util\grpc++_test_util.vcxproj">
-      <Project>{0BE77741-552A-929B-A497-4EF7ECE17A64}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
-      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++\grpc++.vcxproj">
-      <Project>{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
-      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
-      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
-      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_config\grpc++_test_config.vcxproj">
-      <Project>{3F7D093D-11F9-C4BC-BEB7-18EB28E3F290}</Project>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\interop_server_lib\interop_server_lib.vcxproj">
+      <Project>{458DCA09-83B9-5E68-D7E9-118864ECBD94}</Project>
     </ProjectReference>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/vsprojects/vcxproj/interop_server_main/interop_server_main.vcxproj.filters b/vsprojects/vcxproj/interop_server_main/interop_server_main.vcxproj.filters
index 4ee8135..d8d0492 100644
--- a/vsprojects/vcxproj/interop_server_main/interop_server_main.vcxproj.filters
+++ b/vsprojects/vcxproj/interop_server_main/interop_server_main.vcxproj.filters
@@ -1,33 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\empty.proto">
-      <Filter>src\proto\grpc\testing</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\messages.proto">
-      <Filter>src\proto\grpc\testing</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\test.proto">
-      <Filter>src\proto\grpc\testing</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\cpp\interop\interop_server.cc">
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\interop\interop_server_bootstrap.cc">
       <Filter>test\cpp\interop</Filter>
     </ClCompile>
   </ItemGroup>
 
   <ItemGroup>
-    <Filter Include="src">
-      <UniqueIdentifier>{9dfb04b3-9e58-7efb-70a2-b02ec8c5e83e}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="src\proto">
-      <UniqueIdentifier>{ebd8177f-6130-a4fb-1c41-d894f801e3b9}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="src\proto\grpc">
-      <UniqueIdentifier>{1df1acf2-4654-4530-10af-912381c69012}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="src\proto\grpc\testing">
-      <UniqueIdentifier>{ba56d830-1546-c07f-f5ee-03164e41914e}</UniqueIdentifier>
-    </Filter>
     <Filter Include="test">
       <UniqueIdentifier>{02523054-816a-75a0-b24b-f527e99c7142}</UniqueIdentifier>
     </Filter>
diff --git a/vsprojects/vcxproj/test/census_resource_test/census_resource_test.vcxproj b/vsprojects/vcxproj/test/census_resource_test/census_resource_test.vcxproj
new file mode 100644
index 0000000..c4fbd54
--- /dev/null
+++ b/vsprojects/vcxproj/test/census_resource_test/census_resource_test.vcxproj
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{18CF99B5-3C61-EC3D-9509-3C95334C3B88}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>census_resource_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>census_resource_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\census\resource_test.c">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
+      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/census_resource_test/census_resource_test.vcxproj.filters b/vsprojects/vcxproj/test/census_resource_test/census_resource_test.vcxproj.filters
new file mode 100644
index 0000000..93faac5
--- /dev/null
+++ b/vsprojects/vcxproj/test/census_resource_test/census_resource_test.vcxproj.filters
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\census\resource_test.c">
+      <Filter>test\core\census</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{313aad4e-d33b-88c5-7d94-e04a4cb0c912}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{ff2d74ef-228a-1739-7fa7-7dccbec5b0c5}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\census">
+      <UniqueIdentifier>{4f529bd9-396f-027c-bc49-f9a6bbc97c72}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/census_trace_context_test/census_trace_context_test.vcxproj b/vsprojects/vcxproj/test/census_trace_context_test/census_trace_context_test.vcxproj
new file mode 100644
index 0000000..f0c76e7
--- /dev/null
+++ b/vsprojects/vcxproj/test/census_trace_context_test/census_trace_context_test.vcxproj
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{14511310-FAE4-C287-31DC-D6F456D10AF3}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>census_trace_context_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>census_trace_context_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\census\trace_context_test.c">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
+      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/census_trace_context_test/census_trace_context_test.vcxproj.filters b/vsprojects/vcxproj/test/census_trace_context_test/census_trace_context_test.vcxproj.filters
new file mode 100644
index 0000000..1aafe06
--- /dev/null
+++ b/vsprojects/vcxproj/test/census_trace_context_test/census_trace_context_test.vcxproj.filters
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\census\trace_context_test.c">
+      <Filter>test\core\census</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{ceb3b75d-b5bc-0966-6724-06fb51237d08}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{8d294ce5-d65c-2fef-28ab-43b4d23722c3}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\census">
+      <UniqueIdentifier>{3981af30-20c0-647e-755f-fa184674d50a}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj b/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj
index e608f7e..2d75735 100644
--- a/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj
+++ b/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj
@@ -183,6 +183,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\stub_options.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync.h" />
@@ -190,27 +191,23 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_no_cxx11.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_stream.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\control.pb.cc">
diff --git a/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj.filters b/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj.filters
index 8fc8f39..dda7967 100644
--- a/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj.filters
+++ b/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj.filters
@@ -90,6 +90,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
@@ -111,9 +114,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -132,9 +132,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -147,7 +144,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h">
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h">
@@ -156,9 +153,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -171,9 +165,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
   </ItemGroup>
 
   <ItemGroup>
diff --git a/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj b/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj
index 3331c61..270c2a3 100644
--- a/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj
+++ b/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj
@@ -183,6 +183,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\stub_options.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync.h" />
@@ -190,27 +191,23 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_no_cxx11.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_stream.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\control.pb.cc">
diff --git a/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj.filters b/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj.filters
index a8ff115..dc5e321 100644
--- a/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj.filters
+++ b/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj.filters
@@ -93,6 +93,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
@@ -114,9 +117,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -135,9 +135,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\alloc.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -150,7 +147,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\log.h">
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h">
@@ -159,9 +156,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice_buffer.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
@@ -174,9 +168,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h">
       <Filter>include\grpc\impl\codegen</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\time.h">
-      <Filter>include\grpc\impl\codegen</Filter>
-    </ClInclude>
   </ItemGroup>
 
   <ItemGroup>
diff --git a/vsprojects/vcxproj/test/combiner_test/combiner_test.vcxproj b/vsprojects/vcxproj/test/combiner_test/combiner_test.vcxproj
new file mode 100644
index 0000000..f889d21
--- /dev/null
+++ b/vsprojects/vcxproj/test/combiner_test/combiner_test.vcxproj
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{C237D1E4-8825-80BA-1FC3-5E147E53E96E}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>combiner_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>combiner_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\iomgr\combiner_test.c">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
+      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/combiner_test/combiner_test.vcxproj.filters b/vsprojects/vcxproj/test/combiner_test/combiner_test.vcxproj.filters
new file mode 100644
index 0000000..e8ebb09
--- /dev/null
+++ b/vsprojects/vcxproj/test/combiner_test/combiner_test.vcxproj.filters
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\iomgr\combiner_test.c">
+      <Filter>test\core\iomgr</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{82bca2af-d499-b405-fd05-4d345372496c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{c32d8e20-b719-532d-ba23-bd9d523fac15}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\iomgr">
+      <UniqueIdentifier>{b4fa8ca1-e6c7-dec5-6d62-8a62396825c6}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/connection_refused_test/connection_refused_test.vcxproj b/vsprojects/vcxproj/test/connection_refused_test/connection_refused_test.vcxproj
new file mode 100644
index 0000000..2f13d09
--- /dev/null
+++ b/vsprojects/vcxproj/test/connection_refused_test/connection_refused_test.vcxproj
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{961DFABF-18F2-7D46-4D69-B82A5E9A78B2}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>connection_refused_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>connection_refused_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\connection_refused_test.c">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
+      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/connection_refused_test/connection_refused_test.vcxproj.filters b/vsprojects/vcxproj/test/connection_refused_test/connection_refused_test.vcxproj.filters
new file mode 100644
index 0000000..241a2e3
--- /dev/null
+++ b/vsprojects/vcxproj/test/connection_refused_test/connection_refused_test.vcxproj.filters
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\connection_refused_test.c">
+      <Filter>test\core\end2end</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{4b0c4345-e702-8911-ab5a-c8fb187ff62a}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{bf069703-6525-081f-b965-b73017288d02}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\end2end">
+      <UniqueIdentifier>{4700e535-ec1c-027a-7d48-afa5ba66e3d3}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_census_nosec_test/h2_census_nosec_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_census_nosec_test/h2_census_nosec_test.vcxproj
index 3000ec1..b43c39e 100644
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_census_nosec_test/h2_census_nosec_test.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_census_nosec_test/h2_census_nosec_test.vcxproj
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -54,7 +53,6 @@
   <ImportGroup Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
     <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
   </ImportGroup>
@@ -63,15 +61,11 @@
     <TargetName>h2_census_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)'=='Release'">
     <TargetName>h2_census_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
     <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
@@ -185,8 +179,6 @@
   <ImportGroup Label="ExtensionTargets">
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
   </ImportGroup>
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
@@ -194,9 +186,6 @@
     </PropertyGroup>
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
   </Target>
 </Project>
 
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_compress_nosec_test/h2_compress_nosec_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_compress_nosec_test/h2_compress_nosec_test.vcxproj
index 0fddf55..7da53a3 100644
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_compress_nosec_test/h2_compress_nosec_test.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_compress_nosec_test/h2_compress_nosec_test.vcxproj
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -54,7 +53,6 @@
   <ImportGroup Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
     <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
   </ImportGroup>
@@ -63,15 +61,11 @@
     <TargetName>h2_compress_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)'=='Release'">
     <TargetName>h2_compress_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
     <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
@@ -185,8 +179,6 @@
   <ImportGroup Label="ExtensionTargets">
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
   </ImportGroup>
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
@@ -194,9 +186,6 @@
     </PropertyGroup>
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
   </Target>
 </Project>
 
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_full+trace_nosec_test/h2_full+trace_nosec_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_full+trace_nosec_test/h2_full+trace_nosec_test.vcxproj
index a59e082..bbd7d38 100644
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_full+trace_nosec_test/h2_full+trace_nosec_test.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_full+trace_nosec_test/h2_full+trace_nosec_test.vcxproj
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -54,7 +53,6 @@
   <ImportGroup Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
     <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
   </ImportGroup>
@@ -63,15 +61,11 @@
     <TargetName>h2_full+trace_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)'=='Release'">
     <TargetName>h2_full+trace_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
     <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
@@ -185,8 +179,6 @@
   <ImportGroup Label="ExtensionTargets">
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
   </ImportGroup>
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
@@ -194,9 +186,6 @@
     </PropertyGroup>
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
   </Target>
 </Project>
 
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_full_nosec_test/h2_full_nosec_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_full_nosec_test/h2_full_nosec_test.vcxproj
index b76f51e..c55e07e 100644
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_full_nosec_test/h2_full_nosec_test.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_full_nosec_test/h2_full_nosec_test.vcxproj
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -54,7 +53,6 @@
   <ImportGroup Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
     <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
   </ImportGroup>
@@ -63,15 +61,11 @@
     <TargetName>h2_full_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)'=='Release'">
     <TargetName>h2_full_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
     <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
@@ -185,8 +179,6 @@
   <ImportGroup Label="ExtensionTargets">
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
   </ImportGroup>
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
@@ -194,9 +186,6 @@
     </PropertyGroup>
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
   </Target>
 </Project>
 
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_http_proxy_nosec_test/h2_http_proxy_nosec_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_http_proxy_nosec_test/h2_http_proxy_nosec_test.vcxproj
new file mode 100644
index 0000000..d63a16c
--- /dev/null
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_http_proxy_nosec_test/h2_http_proxy_nosec_test.vcxproj
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{58EA8DAE-6E50-45A3-0CCC-5165D824380E}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>h2_http_proxy_nosec_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>h2_http_proxy_nosec_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_http_proxy.c">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\test/end2end/tests\end2end_nosec_tests\end2end_nosec_tests.vcxproj">
+      <Project>{47C2CB41-4E9F-58B6-F606-F6FAED5D00ED}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util_unsecure\grpc_test_util_unsecure.vcxproj">
+      <Project>{0A7E7F92-FDEA-40F1-A9EC-3BA484F98BBF}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_unsecure\grpc_unsecure.vcxproj">
+      <Project>{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_http_proxy_nosec_test/h2_http_proxy_nosec_test.vcxproj.filters b/vsprojects/vcxproj/test/end2end/fixtures/h2_http_proxy_nosec_test/h2_http_proxy_nosec_test.vcxproj.filters
new file mode 100644
index 0000000..c5e39d1
--- /dev/null
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_http_proxy_nosec_test/h2_http_proxy_nosec_test.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_http_proxy.c">
+      <Filter>test\core\end2end\fixtures</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{318aaada-bfb3-a91b-9ec7-50f91d2553fe}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{6ac28876-f5a4-067a-c4f3-a40fea67e0d8}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\end2end">
+      <UniqueIdentifier>{116ee744-756f-b125-d1fa-367f891887c7}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\end2end\fixtures">
+      <UniqueIdentifier>{48a717b7-dce8-8e65-4a42-cc4396074f3a}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_http_proxy_test/h2_http_proxy_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_http_proxy_test/h2_http_proxy_test.vcxproj
new file mode 100644
index 0000000..50d5982
--- /dev/null
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_http_proxy_test/h2_http_proxy_test.vcxproj
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{C9076E5F-7297-67C2-F786-3CC4F26D8F8A}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>h2_http_proxy_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>h2_http_proxy_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_http_proxy.c">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\test/end2end/tests\end2end_tests\end2end_tests.vcxproj">
+      <Project>{1F1F9084-2A93-B80E-364F-5754894AFAB4}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
+      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_http_proxy_test/h2_http_proxy_test.vcxproj.filters b/vsprojects/vcxproj/test/end2end/fixtures/h2_http_proxy_test/h2_http_proxy_test.vcxproj.filters
new file mode 100644
index 0000000..e344118
--- /dev/null
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_http_proxy_test/h2_http_proxy_test.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_http_proxy.c">
+      <Filter>test\core\end2end\fixtures</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{b725acb1-7722-0738-5c04-6b70cf1f75a4}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{4a10c467-2c7d-9051-e9bf-a24684cfa755}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\end2end">
+      <UniqueIdentifier>{4837a689-69d2-7fd8-99e6-d7a44dd77a6d}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\end2end\fixtures">
+      <UniqueIdentifier>{e7d5a1d8-129c-4b79-0afe-3f453bcd9c49}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_nosec_test/h2_load_reporting_nosec_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_nosec_test/h2_load_reporting_nosec_test.vcxproj
new file mode 100644
index 0000000..7eacfbd
--- /dev/null
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_nosec_test/h2_load_reporting_nosec_test.vcxproj
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{4B9EBBAE-D838-EC09-0B10-2D4520FBC0FF}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>h2_load_reporting_nosec_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>h2_load_reporting_nosec_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_load_reporting.c">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\test/end2end/tests\end2end_nosec_tests\end2end_nosec_tests.vcxproj">
+      <Project>{47C2CB41-4E9F-58B6-F606-F6FAED5D00ED}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util_unsecure\grpc_test_util_unsecure.vcxproj">
+      <Project>{0A7E7F92-FDEA-40F1-A9EC-3BA484F98BBF}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_unsecure\grpc_unsecure.vcxproj">
+      <Project>{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_nosec_test/h2_load_reporting_nosec_test.vcxproj.filters b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_nosec_test/h2_load_reporting_nosec_test.vcxproj.filters
new file mode 100644
index 0000000..72df23e
--- /dev/null
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_nosec_test/h2_load_reporting_nosec_test.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_load_reporting.c">
+      <Filter>test\core\end2end\fixtures</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{abff9aef-586e-65d2-beb8-e6392ecf7440}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{fd31cbf1-8cff-2e6a-618d-a01855997839}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\end2end">
+      <UniqueIdentifier>{c6cc113f-ddb4-733b-5c10-8f98aa2d7d22}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\end2end\fixtures">
+      <UniqueIdentifier>{2cd15a3b-a7e6-b847-b6c4-7c6cc03eacc2}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_test/h2_load_reporting_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_test/h2_load_reporting_test.vcxproj
new file mode 100644
index 0000000..d5d1a15
--- /dev/null
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_test/h2_load_reporting_test.vcxproj
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{F0A06723-2E3E-FE97-34B7-A2BA26D98B83}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>h2_load_reporting_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>h2_load_reporting_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_load_reporting.c">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\test/end2end/tests\end2end_tests\end2end_tests.vcxproj">
+      <Project>{1F1F9084-2A93-B80E-364F-5754894AFAB4}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
+      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_test/h2_load_reporting_test.vcxproj.filters b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_test/h2_load_reporting_test.vcxproj.filters
new file mode 100644
index 0000000..95b049c
--- /dev/null
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_load_reporting_test/h2_load_reporting_test.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_load_reporting.c">
+      <Filter>test\core\end2end\fixtures</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{467ceaa9-3a51-d5df-0556-1ef8e91f5d7d}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{1ec29254-9064-0338-78ca-94d8cfdbd95c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\end2end">
+      <UniqueIdentifier>{58fc84e1-7dc9-a517-359e-7d7e1c417432}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\end2end\fixtures">
+      <UniqueIdentifier>{92ef09e0-592b-0ca5-4d79-5929ae159ed5}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_nosec_test/h2_loadreporting_nosec_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_nosec_test/h2_loadreporting_nosec_test.vcxproj
deleted file mode 100644
index 6a6ac5e..0000000
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_nosec_test/h2_loadreporting_nosec_test.vcxproj
+++ /dev/null
@@ -1,202 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
-  <ItemGroup Label="ProjectConfigurations">
-    <ProjectConfiguration Include="Debug|Win32">
-      <Configuration>Debug</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Debug|x64">
-      <Configuration>Debug</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|Win32">
-      <Configuration>Release</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|x64">
-      <Configuration>Release</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-  </ItemGroup>
-  <PropertyGroup Label="Globals">
-    <ProjectGuid>{679EA55C-7399-53E8-79F0-82FBDB3DDE07}</ProjectGuid>
-    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
-    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
-    <PlatformToolset>v100</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
-    <PlatformToolset>v110</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
-    <PlatformToolset>v120</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
-    <PlatformToolset>v140</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>false</UseDebugLibraries>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
-  <ImportGroup Label="ExtensionSettings">
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
-  </ImportGroup>
-  <PropertyGroup Label="UserMacros" />
-  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
-    <TargetName>h2_loadreporting_nosec_test</TargetName>
-    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
-    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Release'">
-    <TargetName>h2_loadreporting_nosec_test</TargetName>
-    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
-    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
-  </PropertyGroup>
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>MaxSpeed</Optimization>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-    </Link>
-  </ItemDefinitionGroup>
-
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>MaxSpeed</Optimization>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-    </Link>
-  </ItemDefinitionGroup>
-
-  <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_loadreporting.c">
-    </ClCompile>
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\test/end2end/tests\end2end_nosec_tests\end2end_nosec_tests.vcxproj">
-      <Project>{47C2CB41-4E9F-58B6-F606-F6FAED5D00ED}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util_unsecure\grpc_test_util_unsecure.vcxproj">
-      <Project>{0A7E7F92-FDEA-40F1-A9EC-3BA484F98BBF}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_unsecure\grpc_unsecure.vcxproj">
-      <Project>{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
-      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
-      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
-    </ProjectReference>
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="packages.config" />
-  </ItemGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-  <ImportGroup Label="ExtensionTargets">
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
-  </ImportGroup>
-  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
-    <PropertyGroup>
-      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
-    </PropertyGroup>
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
-  </Target>
-</Project>
-
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_nosec_test/h2_loadreporting_nosec_test.vcxproj.filters b/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_nosec_test/h2_loadreporting_nosec_test.vcxproj.filters
deleted file mode 100644
index 4ed1bb0..0000000
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_nosec_test/h2_loadreporting_nosec_test.vcxproj.filters
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_loadreporting.c">
-      <Filter>test\core\end2end\fixtures</Filter>
-    </ClCompile>
-  </ItemGroup>
-
-  <ItemGroup>
-    <Filter Include="test">
-      <UniqueIdentifier>{8adc89fb-e447-77bc-c462-3dba6abcf344}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core">
-      <UniqueIdentifier>{3c2c01f5-2a18-1bee-6ee0-217d415e2a95}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core\end2end">
-      <UniqueIdentifier>{3efa0f41-5802-6a8e-36ee-f246a201a1a5}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core\end2end\fixtures">
-      <UniqueIdentifier>{366eb24f-49e9-d57f-e20f-729d1e0fb892}</UniqueIdentifier>
-    </Filter>
-  </ItemGroup>
-</Project>
-
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_test/h2_loadreporting_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_test/h2_loadreporting_test.vcxproj
deleted file mode 100644
index 2076548..0000000
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_test/h2_loadreporting_test.vcxproj
+++ /dev/null
@@ -1,202 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
-  <ItemGroup Label="ProjectConfigurations">
-    <ProjectConfiguration Include="Debug|Win32">
-      <Configuration>Debug</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Debug|x64">
-      <Configuration>Debug</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|Win32">
-      <Configuration>Release</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|x64">
-      <Configuration>Release</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-  </ItemGroup>
-  <PropertyGroup Label="Globals">
-    <ProjectGuid>{B107130E-EA33-C114-9CB6-78A18C929F64}</ProjectGuid>
-    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
-    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
-    <PlatformToolset>v100</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
-    <PlatformToolset>v110</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
-    <PlatformToolset>v120</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
-    <PlatformToolset>v140</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>false</UseDebugLibraries>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
-  <ImportGroup Label="ExtensionSettings">
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
-  </ImportGroup>
-  <PropertyGroup Label="UserMacros" />
-  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
-    <TargetName>h2_loadreporting_test</TargetName>
-    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
-    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Release'">
-    <TargetName>h2_loadreporting_test</TargetName>
-    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
-    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
-  </PropertyGroup>
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>MaxSpeed</Optimization>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-    </Link>
-  </ItemDefinitionGroup>
-
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>MaxSpeed</Optimization>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-    </Link>
-  </ItemDefinitionGroup>
-
-  <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_loadreporting.c">
-    </ClCompile>
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\test/end2end/tests\end2end_tests\end2end_tests.vcxproj">
-      <Project>{1F1F9084-2A93-B80E-364F-5754894AFAB4}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
-      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
-      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
-      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
-      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
-    </ProjectReference>
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="packages.config" />
-  </ItemGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-  <ImportGroup Label="ExtensionTargets">
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
-  </ImportGroup>
-  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
-    <PropertyGroup>
-      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
-    </PropertyGroup>
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
-  </Target>
-</Project>
-
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_test/h2_loadreporting_test.vcxproj.filters b/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_test/h2_loadreporting_test.vcxproj.filters
deleted file mode 100644
index afe5432..0000000
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_loadreporting_test/h2_loadreporting_test.vcxproj.filters
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\fixtures\h2_loadreporting.c">
-      <Filter>test\core\end2end\fixtures</Filter>
-    </ClCompile>
-  </ItemGroup>
-
-  <ItemGroup>
-    <Filter Include="test">
-      <UniqueIdentifier>{8f73760a-74dc-05ef-65e1-fa8c44ccf918}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core">
-      <UniqueIdentifier>{a280079e-b626-333e-0636-8fe6eb788ca1}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core\end2end">
-      <UniqueIdentifier>{c1aa73d6-503a-06c0-42b2-0793a4805e96}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core\end2end\fixtures">
-      <UniqueIdentifier>{3e738e89-dc27-f929-cc8f-1aa94c24345b}</UniqueIdentifier>
-    </Filter>
-  </ItemGroup>
-</Project>
-
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_proxy_nosec_test/h2_proxy_nosec_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_proxy_nosec_test/h2_proxy_nosec_test.vcxproj
index 7eb45e4..d4bca1a 100644
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_proxy_nosec_test/h2_proxy_nosec_test.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_proxy_nosec_test/h2_proxy_nosec_test.vcxproj
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -54,7 +53,6 @@
   <ImportGroup Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
     <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
   </ImportGroup>
@@ -63,15 +61,11 @@
     <TargetName>h2_proxy_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)'=='Release'">
     <TargetName>h2_proxy_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
     <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
@@ -185,8 +179,6 @@
   <ImportGroup Label="ExtensionTargets">
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
   </ImportGroup>
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
@@ -194,9 +186,6 @@
     </PropertyGroup>
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
   </Target>
 </Project>
 
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_sockpair+trace_nosec_test/h2_sockpair+trace_nosec_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_sockpair+trace_nosec_test/h2_sockpair+trace_nosec_test.vcxproj
index ebcdb21..7159d07 100644
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_sockpair+trace_nosec_test/h2_sockpair+trace_nosec_test.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_sockpair+trace_nosec_test/h2_sockpair+trace_nosec_test.vcxproj
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -54,7 +53,6 @@
   <ImportGroup Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
     <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
   </ImportGroup>
@@ -63,15 +61,11 @@
     <TargetName>h2_sockpair+trace_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)'=='Release'">
     <TargetName>h2_sockpair+trace_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
     <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
@@ -185,8 +179,6 @@
   <ImportGroup Label="ExtensionTargets">
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
   </ImportGroup>
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
@@ -194,9 +186,6 @@
     </PropertyGroup>
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
   </Target>
 </Project>
 
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_sockpair_1byte_nosec_test/h2_sockpair_1byte_nosec_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_sockpair_1byte_nosec_test/h2_sockpair_1byte_nosec_test.vcxproj
index a3529e5..6d2162e 100644
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_sockpair_1byte_nosec_test/h2_sockpair_1byte_nosec_test.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_sockpair_1byte_nosec_test/h2_sockpair_1byte_nosec_test.vcxproj
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -54,7 +53,6 @@
   <ImportGroup Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
     <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
   </ImportGroup>
@@ -63,15 +61,11 @@
     <TargetName>h2_sockpair_1byte_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)'=='Release'">
     <TargetName>h2_sockpair_1byte_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
     <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
@@ -185,8 +179,6 @@
   <ImportGroup Label="ExtensionTargets">
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
   </ImportGroup>
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
@@ -194,9 +186,6 @@
     </PropertyGroup>
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
   </Target>
 </Project>
 
diff --git a/vsprojects/vcxproj/test/end2end/fixtures/h2_sockpair_nosec_test/h2_sockpair_nosec_test.vcxproj b/vsprojects/vcxproj/test/end2end/fixtures/h2_sockpair_nosec_test/h2_sockpair_nosec_test.vcxproj
index fe01a3d..d33b850 100644
--- a/vsprojects/vcxproj/test/end2end/fixtures/h2_sockpair_nosec_test/h2_sockpair_nosec_test.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/fixtures/h2_sockpair_nosec_test/h2_sockpair_nosec_test.vcxproj
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -54,7 +53,6 @@
   <ImportGroup Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
     <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
     <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
   </ImportGroup>
@@ -63,15 +61,11 @@
     <TargetName>h2_sockpair_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)'=='Release'">
     <TargetName>h2_sockpair_nosec_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
-    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
-    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
     <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
@@ -185,8 +179,6 @@
   <ImportGroup Label="ExtensionTargets">
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
   <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
-  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
   </ImportGroup>
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
@@ -194,9 +186,6 @@
     </PropertyGroup>
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
     <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
-    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
   </Target>
 </Project>
 
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj
index 28ced2d..d6ec369 100644
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj
@@ -179,6 +179,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\empty_batch.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_call_init_fails.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_causes_close.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\graceful_server_shutdown.c">
@@ -193,6 +195,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_message_length.c">
@@ -201,6 +205,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\network_status_change.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_logging.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_op.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\payload.c">
@@ -221,6 +227,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_tags.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_cacheable_request.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_delayed_request.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_metadata.c">
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters
index 7c72535..a6c24a8 100644
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters
+++ b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters
@@ -43,6 +43,9 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\empty_batch.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_call_init_fails.c">
+      <Filter>test\core\end2end\tests</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_causes_close.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
@@ -64,6 +67,9 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
+      <Filter>test\core\end2end\tests</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
@@ -76,6 +82,9 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\network_status_change.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_logging.c">
+      <Filter>test\core\end2end\tests</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_op.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
@@ -106,6 +115,9 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_tags.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_cacheable_request.c">
+      <Filter>test\core\end2end\tests</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_delayed_request.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj
index bc064cd..085b147 100644
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj
+++ b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj
@@ -181,6 +181,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\empty_batch.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_call_init_fails.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_causes_close.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\graceful_server_shutdown.c">
@@ -195,6 +197,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_message_length.c">
@@ -203,6 +207,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\network_status_change.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_logging.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_op.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\payload.c">
@@ -223,6 +229,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_tags.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_cacheable_request.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_delayed_request.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_metadata.c">
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters
index d959c80..d224a9c 100644
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters
+++ b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters
@@ -46,6 +46,9 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\empty_batch.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_call_init_fails.c">
+      <Filter>test\core\end2end\tests</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_causes_close.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
@@ -67,6 +70,9 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
+      <Filter>test\core\end2end\tests</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
@@ -79,6 +85,9 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\network_status_change.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_logging.c">
+      <Filter>test\core\end2end\tests</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_op.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
@@ -109,6 +118,9 @@
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_tags.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_cacheable_request.c">
+      <Filter>test\core\end2end\tests</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_delayed_request.c">
       <Filter>test\core\end2end\tests</Filter>
     </ClCompile>
diff --git a/vsprojects/vcxproj/test/filter_end2end_test/filter_end2end_test.vcxproj b/vsprojects/vcxproj/test/filter_end2end_test/filter_end2end_test.vcxproj
new file mode 100644
index 0000000..99c000e
--- /dev/null
+++ b/vsprojects/vcxproj/test/filter_end2end_test/filter_end2end_test.vcxproj
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{1D42975A-18A5-09D0-30B1-2AE6A97FB9DE}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\cpptest.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\protobuf.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>filter_end2end_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>filter_end2end_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\end2end\filter_end2end_test.cc">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_util\grpc++_test_util.vcxproj">
+      <Project>{0BE77741-552A-929B-A497-4EF7ECE17A64}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
+      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++\grpc++.vcxproj">
+      <Project>{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/filter_end2end_test/filter_end2end_test.vcxproj.filters b/vsprojects/vcxproj/test/filter_end2end_test/filter_end2end_test.vcxproj.filters
new file mode 100644
index 0000000..c684423
--- /dev/null
+++ b/vsprojects/vcxproj/test/filter_end2end_test/filter_end2end_test.vcxproj.filters
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\end2end\filter_end2end_test.cc">
+      <Filter>test\cpp\end2end</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{f7581160-220d-1118-f29e-6b37e45671c9}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\cpp">
+      <UniqueIdentifier>{63784d35-03cc-1a7e-0c95-a9451bc1daf4}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\cpp\end2end">
+      <UniqueIdentifier>{d2a01682-970e-d23d-1eb8-ef020e3a1ca3}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/gpr_mpscq_test/gpr_mpscq_test.vcxproj b/vsprojects/vcxproj/test/gpr_mpscq_test/gpr_mpscq_test.vcxproj
new file mode 100644
index 0000000..0134286
--- /dev/null
+++ b/vsprojects/vcxproj/test/gpr_mpscq_test/gpr_mpscq_test.vcxproj
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{B3D7760B-8BEA-2EF6-F1D4-9F9020E166D6}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>gpr_mpscq_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>gpr_mpscq_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\support\mpscq_test.c">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/gpr_mpscq_test/gpr_mpscq_test.vcxproj.filters b/vsprojects/vcxproj/test/gpr_mpscq_test/gpr_mpscq_test.vcxproj.filters
new file mode 100644
index 0000000..9cceb9f
--- /dev/null
+++ b/vsprojects/vcxproj/test/gpr_mpscq_test/gpr_mpscq_test.vcxproj.filters
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\support\mpscq_test.c">
+      <Filter>test\core\support</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{e82fb80c-10ba-2959-55d6-8653715f1e4f}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{32120233-25e6-f3e4-f828-c6408d47ec04}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\support">
+      <UniqueIdentifier>{aa3a22bc-229a-c00a-dd4a-924c818c6a49}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/gpr_percent_encoding_test/gpr_percent_encoding_test.vcxproj b/vsprojects/vcxproj/test/gpr_percent_encoding_test/gpr_percent_encoding_test.vcxproj
new file mode 100644
index 0000000..1a943e4
--- /dev/null
+++ b/vsprojects/vcxproj/test/gpr_percent_encoding_test/gpr_percent_encoding_test.vcxproj
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{8313AE17-FCFA-8110-95C7-7AF2F814D188}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>gpr_percent_encoding_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>gpr_percent_encoding_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\support\percent_encoding_test.c">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/gpr_percent_encoding_test/gpr_percent_encoding_test.vcxproj.filters b/vsprojects/vcxproj/test/gpr_percent_encoding_test/gpr_percent_encoding_test.vcxproj.filters
new file mode 100644
index 0000000..e25b1ad
--- /dev/null
+++ b/vsprojects/vcxproj/test/gpr_percent_encoding_test/gpr_percent_encoding_test.vcxproj.filters
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\support\percent_encoding_test.c">
+      <Filter>test\core\support</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{c2ea1ce7-c916-11e7-6477-92a18d988d54}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{14fa1f36-5dea-ee3f-d3fa-a137176b235f}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\support">
+      <UniqueIdentifier>{e245dffe-593f-f63e-8dc8-3c9de4d00697}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/grpc_cli/grpc_cli.vcxproj b/vsprojects/vcxproj/test/grpc_cli/grpc_cli.vcxproj
index 9c8cdc5..78a0a63 100644
--- a/vsprojects/vcxproj/test/grpc_cli/grpc_cli.vcxproj
+++ b/vsprojects/vcxproj/test/grpc_cli/grpc_cli.vcxproj
@@ -167,12 +167,6 @@
     <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_cli_libs\grpc_cli_libs.vcxproj">
       <Project>{86E35862-43E8-F59E-F906-AFE0348AD3D2}</Project>
     </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_util\grpc++_test_util.vcxproj">
-      <Project>{0BE77741-552A-929B-A497-4EF7ECE17A64}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
-      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
-    </ProjectReference>
     <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_reflection\grpc++_reflection.vcxproj">
       <Project>{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}</Project>
     </ProjectReference>
@@ -182,9 +176,6 @@
     <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
       <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
     </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
-      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
-    </ProjectReference>
     <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
       <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
     </ProjectReference>
diff --git a/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj b/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj
new file mode 100644
index 0000000..bdf310d
--- /dev/null
+++ b/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{F00D82D4-E988-6D2F-F0B9-9E82BCC2A2B2}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\cpptest.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\protobuf.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>grpc_tool_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>grpc_tool_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\proto_utils.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\async_stream.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\async_unary_call.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\call.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\call_hook.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\channel_interface.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\client_context.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\client_unary_call.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\completion_queue.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\completion_queue_tag.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\config.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\core_codegen_interface.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\create_auth_context.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\grpc_library.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\method_handler_impl.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\rpc_method.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\rpc_service_method.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\security\auth_context.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\serialization_traits.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_context.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_interface.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\stub_options.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_cxx11.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_no_cxx11.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_stream.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\config_protobuf.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="$(SolutionDir)\..\test\cpp\util\string_ref_helper.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\echo.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\echo.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\echo.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\echo.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\echo_messages.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\echo_messages.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\echo_messages.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\testing\echo_messages.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\grpc_tool_test.cc">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\string_ref_helper.cc">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_cli_libs\grpc_cli_libs.vcxproj">
+      <Project>{86E35862-43E8-F59E-F906-AFE0348AD3D2}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_reflection\grpc++_reflection.vcxproj">
+      <Project>{5F575402-3F89-5D1A-6910-9DB8BF5D2BAB}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
+      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++\grpc++.vcxproj">
+      <Project>{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj.filters b/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj.filters
new file mode 100644
index 0000000..cd341e9
--- /dev/null
+++ b/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj.filters
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\echo.proto">
+      <Filter>src\proto\grpc\testing</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\testing\echo_messages.proto">
+      <Filter>src\proto\grpc\testing</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\grpc_tool_test.cc">
+      <Filter>test\cpp\util</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\util\string_ref_helper.cc">
+      <Filter>test\cpp\util</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\proto_utils.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\async_stream.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\async_unary_call.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\call.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\call_hook.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\channel_interface.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\client_context.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\client_unary_call.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\completion_queue.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\completion_queue_tag.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\config.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\core_codegen_interface.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\create_auth_context.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\grpc_library.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\method_handler_impl.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\rpc_method.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\rpc_service_method.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\security\auth_context.h">
+      <Filter>include\grpc++\impl\codegen\security</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\serialization_traits.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_context.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_interface.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\string_ref.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\stub_options.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_cxx11.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_no_cxx11.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\sync_stream.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\time.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\byte_buffer_reader.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\compression_types.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\connectivity_state.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\grpc_types.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\propagation_bits.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\status.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_atomic.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_gcc_sync.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\atm_windows.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\gpr_types.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\port_platform.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\slice.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_generic.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_posix.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc\impl\codegen\sync_windows.h">
+      <Filter>include\grpc\impl\codegen</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\config_protobuf.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="$(SolutionDir)\..\test\cpp\util\string_ref_helper.h">
+      <Filter>test\cpp\util</Filter>
+    </ClInclude>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="include">
+      <UniqueIdentifier>{89fed779-17c5-23da-c8a2-9e868ff34480}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="include\grpc">
+      <UniqueIdentifier>{96e4a1a8-0b91-1a6d-ae4d-ddf33abb93c0}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="include\grpc++">
+      <UniqueIdentifier>{1d9dcc6f-7c1b-cdc3-4c35-73d5968dfd92}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="include\grpc++\impl">
+      <UniqueIdentifier>{5eca7690-973a-c8ed-84d6-5325f8de43ac}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="include\grpc++\impl\codegen">
+      <UniqueIdentifier>{5789073e-5b84-0ec9-af06-47866647874d}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="include\grpc++\impl\codegen\security">
+      <UniqueIdentifier>{d3f3293f-204f-7771-fcdf-de673f6b06b6}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="include\grpc\impl">
+      <UniqueIdentifier>{7e90f37b-f9cc-0725-b2c1-12aa7d4809ba}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="include\grpc\impl\codegen">
+      <UniqueIdentifier>{7e4b71ef-8125-6446-bfc1-9bc90beed59c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src">
+      <UniqueIdentifier>{169774bd-5c6c-6827-66a4-326b4aef44d6}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto">
+      <UniqueIdentifier>{1b609b37-ef2a-e5eb-e1ba-ad9e79c77438}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto\grpc">
+      <UniqueIdentifier>{cd1e35d8-8a61-62fe-6ce1-c8936872d1ef}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto\grpc\testing">
+      <UniqueIdentifier>{f7ee4df5-1f47-1e7f-c91e-350382c1b729}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test">
+      <UniqueIdentifier>{f2166b83-6b0b-d53b-b58b-627bd9efcad2}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\cpp">
+      <UniqueIdentifier>{bbe36cbc-7fbe-2817-0bd0-d03726f323e6}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\cpp\util">
+      <UniqueIdentifier>{e106cd7b-cfa0-0645-f1a9-2acedc23afe7}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/grpclb_test/grpclb_test.vcxproj b/vsprojects/vcxproj/test/grpclb_test/grpclb_test.vcxproj
new file mode 100644
index 0000000..91b9a6e
--- /dev/null
+++ b/vsprojects/vcxproj/test/grpclb_test/grpclb_test.vcxproj
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" />
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{9D6FFE17-ABF0-A851-268E-9E3E8C573CBB}</ProjectGuid>
+    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
+    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
+    <PlatformToolset>v100</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(SolutionDir)\..\vsprojects\cpptest.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\openssl.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\protobuf.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
+    <Import Project="$(SolutionDir)\..\vsprojects\zlib.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <TargetName>grpclb_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <TargetName>grpclb_test</TargetName>
+    <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
+    <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
+    <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
+    <Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl>
+  </PropertyGroup>
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
+      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
+      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\lb\v1\load_balancer.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\lb\v1\load_balancer.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\lb\v1\load_balancer.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="$(SolutionDir)\..\src\proto\grpc\lb\v1\load_balancer.grpc.pb.h">
+    </ClInclude>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\grpclb\grpclb_test.cc">
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
+      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
+      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++\grpc++.vcxproj">
+      <Project>{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc++_test_util\grpc++_test_util.vcxproj">
+      <Project>{0BE77741-552A-929B-A497-4EF7ECE17A64}</Project>
+    </ProjectReference>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
+      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  <Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" />
+  </ImportGroup>
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" />
+    <Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" />
+  </Target>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/grpclb_test/grpclb_test.vcxproj.filters b/vsprojects/vcxproj/test/grpclb_test/grpclb_test.vcxproj.filters
new file mode 100644
index 0000000..5493df2
--- /dev/null
+++ b/vsprojects/vcxproj/test/grpclb_test/grpclb_test.vcxproj.filters
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\src\proto\grpc\lb\v1\load_balancer.proto">
+      <Filter>src\proto\grpc\lb\v1</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\test\cpp\grpclb\grpclb_test.cc">
+      <Filter>test\cpp\grpclb</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="src">
+      <UniqueIdentifier>{082bbcbc-bbd8-db7d-afe9-fa0fec76863d}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto">
+      <UniqueIdentifier>{7c61995a-80c2-ef3d-0a4b-baf2221c12f8}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto\grpc">
+      <UniqueIdentifier>{fc2920d4-902c-fdf0-d4b8-6b22e1361105}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto\grpc\lb">
+      <UniqueIdentifier>{4778352a-559f-b008-e3d2-4ae9181260df}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\proto\grpc\lb\v1">
+      <UniqueIdentifier>{36a4326b-4f2a-4720-b730-e40fe078fd40}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test">
+      <UniqueIdentifier>{e0ba55a2-37d9-5029-6f4e-64f097307340}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\cpp">
+      <UniqueIdentifier>{ad67d6ba-d08b-7879-2929-20f0f01c9918}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\cpp\grpclb">
+      <UniqueIdentifier>{4498fa30-2dab-584c-5711-a1055cec30f6}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+
diff --git a/vsprojects/vcxproj/test/timeout_encoding_test/timeout_encoding_test.vcxproj b/vsprojects/vcxproj/test/timeout_encoding_test/timeout_encoding_test.vcxproj
index 37be682..76efe30 100644
--- a/vsprojects/vcxproj/test/timeout_encoding_test/timeout_encoding_test.vcxproj
+++ b/vsprojects/vcxproj/test/timeout_encoding_test/timeout_encoding_test.vcxproj
@@ -158,7 +158,7 @@
   </ItemDefinitionGroup>
 
   <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\transport\chttp2\timeout_encoding_test.c">
+    <ClCompile Include="$(SolutionDir)\..\test\core\transport\timeout_encoding_test.c">
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
diff --git a/vsprojects/vcxproj/test/timeout_encoding_test/timeout_encoding_test.vcxproj.filters b/vsprojects/vcxproj/test/timeout_encoding_test/timeout_encoding_test.vcxproj.filters
index 3e3b8d2..d5fafc2 100644
--- a/vsprojects/vcxproj/test/timeout_encoding_test/timeout_encoding_test.vcxproj.filters
+++ b/vsprojects/vcxproj/test/timeout_encoding_test/timeout_encoding_test.vcxproj.filters
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\transport\chttp2\timeout_encoding_test.c">
-      <Filter>test\core\transport\chttp2</Filter>
+    <ClCompile Include="$(SolutionDir)\..\test\core\transport\timeout_encoding_test.c">
+      <Filter>test\core\transport</Filter>
     </ClCompile>
   </ItemGroup>
 
@@ -16,9 +16,6 @@
     <Filter Include="test\core\transport">
       <UniqueIdentifier>{3178e0c5-5a68-26eb-6bf6-a5a8dabb0a3c}</UniqueIdentifier>
     </Filter>
-    <Filter Include="test\core\transport\chttp2">
-      <UniqueIdentifier>{40739011-4ce0-83f5-d2d6-af515bac7f87}</UniqueIdentifier>
-    </Filter>
   </ItemGroup>
 </Project>